/**
 * Primary entry file to the app
 */
import type { FastifyBaseLogger, FastifyInstance } from "fastify"
import fastifyCors from "@fastify/cors"
import fastifySchedule from "@fastify/schedule"
import fastifySensible from "@fastify/sensible"
import Fastify from "fastify"
import healthcheck from "fastify-custom-healthcheck"
import { dirname } from "path"
import pino from "pino"
import { Transform } from "stream"
import { fileURLToPath } from "url"

// Application components
import registerCore from "./domains"
import { registerPlugins } from "./core/pluginSystem/pluginLoader"
import initializeSocketSystem from "./core/sockets/socketManager"
import jwt from "./modules/authentication"
import rateLimiter from "./modules/hooks/rate-limiter"
import eventbus from "./core/eventBus"
import headersPlugin from "./core/http/headers"
import httpClient from "./core/http/httpService"
import requestParamsPlugin from "./core/http/request-params.plugin"
import mongodb from "./modules/database/providers/mongodb/mongoConnection"
import valkeyPlugin from "./modules/cache/providers/valkey/valkeyConnection"
import socket from "./core/sockets"
import taskManager from "./core/backgroundTasks/taskManager"
import configService from "./core/config/configService"
import {
  generateAllFrontendConfigs,
  setFastifyInstanceForGeneration,
} from "./core/pluginSystem/configGenerator"
import generateRegisteredSchemas from "./core/validation/schemaGenerator"
import { getAllowedOrigins } from "@/helpers"

// Environment type guard
const NODE_ENV = process.env.NODE_ENV as
  | "development"
  | "production"
  | "test"
  | "internal"

/* Set global variables */
global.__filename = fileURLToPath(import.meta.url)
global.__dirname = dirname(__filename)

/**
 * Logger filter stream to exclude plugin logs when configured
 */
class PluginFilterStream extends Transform {
  constructor() {
    super({ objectMode: true })
  }

  _transform(chunk: any, _: any, callback: any) {
    try {
      const log = JSON.parse(chunk.toString())

      if (!process.env.DISABLE_PLUGIN_LOGS || !log.plugin) {
        this.push(JSON.stringify(log) + "\n")
      }
    } catch {
      // Pass through if parsing fails
      this.push(chunk)
    }
    callback()
  }
}

/**
 * Configure and create the logger
 */
const createLogger = (): FastifyBaseLogger => {
  const isDevelopment = NODE_ENV === "development"
  const usePrettyLogs = process.env.PRETTY_LOGS === "true"
  const logLevel = process.env.LOG_LEVEL ?? "debug"
  const disablePluginLogs = process.env.DISABLE_PLUGIN_LOGS === "true"

  const loggerOptions = {
    level: logLevel,
    ...(isDevelopment || usePrettyLogs
      ? {
          transport: {
            target: "pino-pretty",
            options: {
              colorize: true,
              translateTime: "SYS:standard",
              ignore: "pid,hostname",
            },
          },
        }
      : {
          // Production logging configuration
          serializers: {
            req: pino.stdSerializers.req,
            res: pino.stdSerializers.res,
            err: pino.stdSerializers.err,
          },
        }),
  }

  const logger = disablePluginLogs
    ? pino(loggerOptions, new PluginFilterStream())
    : pino(loggerOptions)

  // Ensure logger is working
  logger.info("Logger initialized successfully")

  return logger
}

/**
 * Main application factory function
 */
export default async (): Promise<FastifyInstance> => {
  const loggerInstance = createLogger()

  const fastify = Fastify({
    loggerInstance,
    disableRequestLogging: true,
    trustProxy: true,
  })

  // Validate required environment variables
  if (!process.env.SERVER_TOKEN_SECRET) {
    fastify.log.fatal("Missing config SERVER_TOKEN_SECRET")
    throw new Error("Canceling...")
  }

  await configService.init()

  // setup route tracker for plugin code generation
  setFastifyInstanceForGeneration(fastify)

  // Register core plugins
  fastify.register(eventbus)
  await fastify.register(mongodb, { uri: process.env.MONGO_HOST ?? "" })
  fastify.register(headersPlugin)
  fastify.register(jwt, { secret: process.env.SERVER_TOKEN_SECRET })
  fastify.register(rateLimiter)

  fastify.register(valkeyPlugin, {
    url: process.env.CACHE_URL,
    options: {
      maxRetriesPerRequest: 3,
      enableReadyCheck: true,
    },
  })
  fastify.register(socket)
  fastify.register(requestParamsPlugin)
  fastify.register(taskManager)

  // Register utility plugins
  fastify.register(fastifySensible)
  fastify.register(fastifySchedule)
  await fastify.register(httpClient, {})

  // Configure CORS
  // Enhanced CORS configuration for streaming support
  fastify.register(fastifyCors, {
    methods: "GET,PUT,HEAD,POST,PATCH,DELETE,OPTIONS",
    strictPreflight: false,
    origin: (origin, callback) => {
      const allowedOrigins = getAllowedOrigins()

      // Allow server-to-server calls (no origin)
      if (!origin) {
        callback(null, true)
        return
      }

      // Check against allowed origins
      const isAllowed = allowedOrigins.some((allowedOrigin) => {
        if (typeof allowedOrigin === "string") {
          return allowedOrigin === origin
        } else if (allowedOrigin instanceof RegExp) {
          return allowedOrigin.test(origin)
        }
        return false
      })

      if (isAllowed) {
        callback(null, true)
      } else {
        fastify.log.warn(`CORS blocked origin: ${origin}`)
        fastify.log.debug(
          'Allowed CORS origins are: ' + allowedOrigins.join(" | "),
        )
        callback(new Error("Not allowed by CORS"), false)
      }
    },
    credentials: true,
    allowedHeaders: [
      "Accept",
      "Accept-Version",
      "Access-Control-Allow-Headers",
      "Access-Control-Request-Method",
      "Authorization",
      "Content-Range",
      "Content-Type",
      "Origin",
      "current-project",
      "app-session-id",
      "app-request-code",
      "app-broadcast-channels",
      "http-client-used",
      "x-work-session-id", // For work session authentication
      "Cache-Control", // For streaming
      "Connection", // For streaming
    ],
    exposedHeaders: [
      "Content-Type",
      "Cache-Control",
      "Connection",
      "Access-Control-Allow-Origin",
      "Access-Control-Allow-Headers",
    ],
    maxAge: 86400, // 24 hours for preflight cache
  })

  // Add health check
  fastify.register(healthcheck, {
    info: {
      env: NODE_ENV,
    },
    schema: { tags: ["core/system"] },
  })

  // Register application components
  await fastify.register(registerCore, { prefix: "/core" })

  // Register plugins
  try {
    await registerPlugins(fastify)
    fastify.log.info("Plugins registered successfully")
  } catch (error: any) {
    fastify.log.error("Plugin registration failed:" + error.message)
  }

  // Log registered routes in debug mode
  fastify.log.debug(fastify.printRoutes())

  // Generate schemas and types
  await generateRegisteredSchemas(fastify)

  await generateAllFrontendConfigs()

  // Initialize socket system
  if (process.env.RACLETTE_BUILD_MODE !== "generate") {
    initializeSocketSystem(fastify)
    await fastify.ready()
    fastify.eventbus.emit("appReady")
  }

  return fastify
}
