import { Router, Request } from "express";
import type { WebSocket } from "ws";
import "reflect-metadata";

interface ControllerInstance {
  [key: string]: unknown;
}

interface ControllerConstructor {
  new (...args: any[]): ControllerInstance;
  prototype: ControllerInstance;
}

export function registerWebSocketControllers(
  router: Router,
  Controller: ControllerConstructor,
  existingInstance?: ControllerInstance,
): void {
  const instance = existingInstance || new Controller();
  const baseRoute = Reflect.getMetadata("baseRoute", Controller) as string;

  Object.getOwnPropertyNames(Controller.prototype).forEach((methodName) => {
    if (methodName === "constructor") return; // Skip the constructor

    const method = Reflect.getMetadata(
      "method",
      instance,
      methodName,
    ) as string;
    const route = Reflect.getMetadata("route", instance, methodName) as string;

    if (method === "ws" && route) {
      const handler = instance[methodName] as unknown;

      if (
        typeof handler === "function" &&
        typeof (router as any).ws === "function"
      ) {
        (router as any).ws(
          `${baseRoute}${route}`,
          (ws: WebSocket, req: Request) => {
            try {
              handler.call(instance, ws, req);
            } catch (error) {
              console.error(
                `WebSocket error in ${Controller.name}.${methodName}`,
                error,
              );
              ws.close(
                1011,
                error instanceof Error
                  ? error.message
                  : "Internal server error",
              );
            }
          },
        );
      }
    }
  });
}

/**
 * Base controller class for WebSocket endpoints
 */
export abstract class BaseWebSocketController {
  protected router: Router;

  constructor() {
    this.router = Router();
  }

  /**
   * Get the base route for this controller
   */
  protected getBaseRoute(): string {
    return Reflect.getMetadata("baseRoute", this.constructor) || "";
  }

  /**
   * Abstract method to handle WebSocket connections
   * Implement this in your derived class
   */
  abstract handleConnection(ws: WebSocket, req: Request): void;
}
