import type { FastifyInstance, preHandlerHookHandler } from "fastify"
import type { Server as SocketServer } from "socket.io"
import type { Schema, Model, Document } from "mongoose"
import type { Listener } from "@c/events/eventEmitter"
import type {
  PluginSchemaDefinition,
  HttpService,
  IntervalTaskOptions,
  CrudHandlers,
  FrontendPayloadRequestData,
  FrontendPayload,
  CacheService,
} from "@/types"
import type { EventEmitter } from "stream"
import type { PluginContract } from "@c/contracting/contractTypes"

export enum PluginType {
  CORE = "core",
  NPM = "npm",
  APP = "app",
}

/**
 * Internal state the plugin loader attaches to `PluginOptions` while a backend
 * plugin boots. Extend with additional nested sections as new loader features
 * need post-plugin hooks.
 */
export type PluginLoaderInternalState = {
  frontendGeneration: {
    explicitCall: boolean
    optedOut: boolean
  }
}

export type PluginOptions = {
  key: string
  pluginName: string
  author: string
  absolutePath: string
  frontendAbsolutePath: string
  type: PluginType
  collectionPrefix: string
  routePrefix: string
  socketIO?: {
    registerEvents?: (io: SocketServer, eventbus: any) => void
    createRooms?: (io: SocketServer) => void
  }
  /**
   * Loader-owned mutable bookkeeping (not part of the public plugin API).
   * Plugin code should ignore; the loader reads this after `index.ts` returns.
   */
  _loaderInternal?: PluginLoaderInternalState
}

/** Options when a plugin customizes frontend generation metadata. */
export type RegisterForFrontendGenerationOptions = {
  routePrefix?: string
  entityMapping?: Record<string, string>
  customConfig?: {
    broadcast?: boolean
    channels?: Record<string, string>
    operationMapping?: Record<string, string>
  }
}

/**
 * Argument to `fastify.registerForFrontendGeneration`.
 * Pass `false` to opt out of frontend generation registration for this plugin.
 */
export type RegisterForFrontendGenerationInput =
  | RegisterForFrontendGenerationOptions
  | false

export type PluginFastifyInstance = {
  /* ROUTES */
  register: (
    routes: (instance: PluginFastifyInstance) => Promise<void> | void,
    opts?: PluginOptions,
  ) => FastifyInstance
  get: FastifyInstance["get"]
  post: FastifyInstance["post"]
  put: FastifyInstance["put"]
  patch: FastifyInstance["patch"]
  delete: FastifyInstance["delete"]
  authenticate: preHandlerHookHandler
  /** Allow JWT or work session (X-Work-Session-Id). Plugin handles permission checks. */
  authenticatedOrWorksession: () => preHandlerHookHandler
  checkPermissions: () => preHandlerHookHandler
  /* LOG */
  log: ReturnType<FastifyInstance["log"]["child"]>
  /* DB */
  createModel: <T extends Document>(
    modelName: string,
    schema: Schema,
  ) => Model<T>
  /* SCHEMA & REGISRATIONS */
  registerSchema: (schemaDefinition: PluginSchemaDefinition) => void
  registerCrudHandlers: <T>(name: string, handlers: CrudHandlers<T>) => void
  registerPayloadHandler: <T extends object>(
    name: string,
    registerData: any,
  ) => void
  /* PAYLOAD */
  createPayload: <T extends object>(
    name: string,
    item: T,
    requestData: FrontendPayloadRequestData,
  ) => Promise<FrontendPayload<T>>
  /* CONNECTION */
  registerSocket: (eventName: string, handler: Listener) => void
  emit: (eventName: string, data: unknown) => void
  http: HttpService
  /* TASKS */
  createIntervalTask: (
    name: string,
    handler: () => Promise<void>,
    options: IntervalTaskOptions,
  ) => void
  cancelTask: (name: string) => void
  // createCacheService: (options: CacheOptions) => CacheService
  cache: CacheService

  eventbus: EventEmitter

  registerPluginContract: (contractData: PluginContract) => void
  registerForFrontendGeneration: (
    options?: RegisterForFrontendGenerationInput,
  ) => void

  fastify: FastifyInstance
  custom: Record<any, any>
}
