import type { WebSocket } from "ws";
import type { Session, User } from "better-auth";
import { WsClientMessageSchema } from "@lila/shared";
import {
  handleLobbyJoin,
  handleLobbyLeave,
  handleLobbyStart,
} from "./handlers/lobbyHandlers.js";
import { handleGameAnswer, handleGameReady } from "./handlers/gameHandlers.js";
import { AppError } from "../errors/AppError.js";

export type AuthenticatedUser = { session: Session; user: User };

const sendError = (ws: WebSocket, code: string, message: string): void => {
  ws.send(JSON.stringify({ type: "error", code, message }));
};

const assertExhaustive = (_: never): never => {
  throw new Error("Unhandled message type");
};

export const handleMessage = async (
  ws: WebSocket,
  rawData: unknown,
  auth: AuthenticatedUser,
): Promise<void> => {
  // Layer 1: parse and validate incoming message
  let parsed: unknown;
  try {
    parsed = JSON.parse(
      typeof rawData === "string" ? rawData : (rawData as Buffer).toString(),
    );
  } catch {
    ws.send(JSON.stringify({ type: "error", message: "Invalid JSON" }));
    return;
  }
  const result = WsClientMessageSchema.safeParse(parsed);

  if (!result.success) {
    ws.send(
      JSON.stringify({ type: "error", message: "Invalid message format" }),
    );
    return;
  }

  const msg = result.data;

  // Layer 2: dispatch to handler, catch and translate errors
  try {
    switch (msg.type) {
      case "lobby:join":
        await handleLobbyJoin(ws, msg, auth.user);
        break;
      case "lobby:leave":
        await handleLobbyLeave(ws, msg, auth.user);
        break;
      case "lobby:start":
        await handleLobbyStart(ws, msg, auth.user);
        break;
      case "game:answer":
        await handleGameAnswer(ws, msg, auth.user);
        break;
      case "game:ready":
        await handleGameReady(ws, msg, auth.user);
        break;
      default:
        assertExhaustive(msg);
    }
  } catch (err) {
    if (err instanceof AppError) {
      sendError(ws, err.name, err.message);
    } else {
      console.error("Unhandled WS error:", err);
      sendError(ws, "INTERNAL_ERROR", "An unexpected error occurred");
    }
  }
};
