// Generated by bynkc — do not edit by hand.
// context chat

import { Ok, Err, Some, None, type Result, type Option, type ValidationError, type Connection, type DurableObjectState, type DurableObjectNamespace, StateRegistry, makeAgent, rehydrationViolation, resolveConnection, connIdOf, acceptHibernatableConnection, newWebSocketPair, webSocketUpgradeResponse, WorkersConnection, type JsonValue, type BoundaryError, type ServiceBinding, callService, boundaryError } from "../../runtime.js";

export type RoomId = string & { readonly __brand: "chat.RoomId" };

export const RoomId = {
  of(value: string): Result<RoomId, ValidationError> {
    return Ok(value as RoomId);
  },
  unsafe(value: string): RoomId {
    return value as RoomId;
  },
};

export type UserId = string & { readonly __brand: "chat.UserId" };

export const UserId = {
  of(value: string): Result<UserId, ValidationError> {
    return Ok(value as UserId);
  },
  unsafe(value: string): UserId {
    return value as UserId;
  },
};

export interface ServerFrame {
  readonly text: string;
}

export const ServerFrame = {
};

export interface ClientFrame {
  readonly text: string;
}

export const ClientFrame = {
};

export const ChatGateway = {
};

export interface RoomState {
  readonly conns: Record<string, string>;
  readonly members: Record<string, boolean>;
}

const __RoomRegistry = new StateRegistry();
function __zeroOfRoomState(): RoomState { return { conns: {}, members: {} }; }

function __rehydrateRoomState(s: RoomState): void {
  for (const __k of Object.keys(s.conns)) { const __r = deserialise_UserId((__k as unknown as JsonValue), "conns"); if (__r.tag === "Err") throw rehydrationViolation("Room", __r.error); }
  for (const __k of Object.keys(s.members)) { const __r = deserialise_UserId((__k as unknown as JsonValue), "members"); if (__r.tag === "Err") throw rehydrationViolation("Room", __r.error); }
}

export class Room {
  state: DurableObjectState;
  constructor(state: DurableObjectState) {
    this.state = state;
  }

  private async loadState(): Promise<RoomState> {
    const stored = await this.state.storage.get<RoomState>("state");
    if (stored === undefined) return __zeroOfRoomState();
    const __merged = { ...__zeroOfRoomState(), ...stored };
    __rehydrateRoomState(__merged);
    return __merged;
  }

  private async commitState(s: RoomState): Promise<void> {
    await this.state.storage.put("state", s);
  }

  async join(u: UserId, conn: Connection<ServerFrame>, deps: {}): Promise<void> {
    const __state = { ...(await this.loadState()) };
    const __result = await (async () => {
      const __r0 = await ((__state.members[u] = true), undefined);
      const __r1 = await ((__state.conns[String(u)] = connIdOf(conn)), undefined);
      return undefined;
    })();
    await this.commitState(__state);
    return __result;
  }

  async leave(u: UserId, deps: {}): Promise<void> {
    const __state = { ...(await this.loadState()) };
    const __result = await (async () => {
      const __r0 = await ((delete __state.members[u]), undefined);
      const __r1 = await (async () => { const __k = String(u); const __cid = __state.conns[__k]; if (__cid !== undefined) { const __c = resolveConnection<ServerFrame>(this.state, __cid); if (__c.tag === "Some") { await __c.value.close(); } delete __state.conns[__k]; } return undefined; })();
      return undefined;
    })();
    await this.commitState(__state);
    return __result;
  }

  async post(sender: UserId, text: string, deps: {}): Promise<void> {
    const __state = await this.loadState();
    const __r0 = await (async () => { await Promise.all(Object.values(__state.conns).flatMap((__cid) => { const __c = resolveConnection<ServerFrame>(this.state, __cid); return __c.tag === "Some" ? [__c.value] : []; }).map((__x) => (async (c: Connection<ServerFrame>) => (c).send({ text: text }))(__x))); })();
    return undefined;
  }

  async __wsOpen_ChatGateway(connection: Connection<ServerFrame>, roomId: RoomId, deps: { identity: UserId }): Promise<void> {
    const __r0 = await (connection).send({ text: "welcome" });
    const __r1 = await this.join(deps.identity, connection, deps);
    return undefined;
  }

  async __wsMessage_ChatGateway(connection: Connection<ServerFrame>, roomId: RoomId, frame: ClientFrame, deps: { identity: UserId }): Promise<void> {
    const __r0 = await this.post(deps.identity, frame.text, deps);
    return undefined;
  }

  async __wsClose_ChatGateway(connection: Connection<ServerFrame>, roomId: RoomId, deps: { identity: UserId }): Promise<void> {
    const __r0 = await this.leave(deps.identity, deps);
    return undefined;
  }

  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    if (url.pathname === "/_bynk/ws/open/ChatGateway") {
      const __pair = newWebSocketPair();
      const __payload = JSON.parse(request.headers.get("X-Bynk-Ws-Open") ?? "{}") as { args: unknown[]; identity: string };
      const connection = acceptHibernatableConnection<ServerFrame>(this.state, __pair.server, { identity: __payload.identity, args: __payload.args });
      await this.__wsOpen_ChatGateway(connection, __payload.args[0] as RoomId, { identity: __payload.identity as UserId });
      return webSocketUpgradeResponse(__pair.client);
    }
    if (url.pathname.startsWith("/_bynk/agent/")) {
      const methodName = url.pathname.slice("/_bynk/agent/".length);
      const { args, deps } = (await request.json()) as { args: unknown[]; deps: unknown };
      const result = await (this as any)[methodName](...args, deps);
      return new Response(JSON.stringify(result), { headers: { "content-type": "application/json" } });
    }
    return new Response("Not Found", { status: 404 });
  }

  async webSocketMessage(ws: { deserializeAttachment(): unknown; send(data: string): void; close(code?: number, reason?: string): void }, message: string | ArrayBuffer): Promise<void> {
    const __att = ws.deserializeAttachment() as { connId: string; identity: string; args: unknown[] } | null;
    if (__att === null) { ws.close(1011, "no session"); return; }
    const connection = new WorkersConnection<ServerFrame>(ws, __att.connId);
    let __raw: string;
    try { __raw = typeof message === "string" ? message : new TextDecoder().decode(message); } catch { ws.close(1003, "unreadable frame"); return; }
    let __json: JsonValue;
    try { __json = JSON.parse(__raw) as JsonValue; } catch { ws.close(1003, "malformed frame"); return; }
    const __dec = deserialise_ClientFrame(__json, "frame");
    if (__dec.tag === "Err") { ws.close(1008, "invalid frame"); return; }
    await this.__wsMessage_ChatGateway(connection, __att.args[0] as RoomId, __dec.value, { identity: __att.identity as UserId });
  }

  async webSocketClose(ws: { deserializeAttachment(): unknown; send(data: string): void; close(code?: number, reason?: string): void }, code: number, reason: string, wasClean: boolean): Promise<void> {
    void code; void reason; void wasClean;
    const __att = ws.deserializeAttachment() as { connId: string; identity: string; args: unknown[] } | null;
    if (__att === null) { ws.close(1011, "no session"); return; }
    const connection = new WorkersConnection<ServerFrame>(ws, __att.connId);
    await this.__wsClose_ChatGateway(connection, __att.args[0] as RoomId, { identity: __att.identity as UserId });
  }

}

export function __makeRoom(key: RoomId, env?: { ROOM?: DurableObjectNamespace }): Room {
  return makeAgent(__RoomRegistry, env?.ROOM, key, (state) => new Room(state));
}

export function __resetAgents(): void {
  __RoomRegistry.reset();
}

export function serialise_ClientFrame(value: ClientFrame): JsonValue {
  return {
    text: value.text as JsonValue,
  };
}

export function deserialise_ClientFrame(json: JsonValue, path: string = "$"): Result<ClientFrame, BoundaryError> {
  if (typeof json !== "object" || json === null || Array.isArray(json)) {
    return Err({ kind: "StructuralMismatch", path, expected: "object", actual: typeof json });
  }
  const obj = json as { [k: string]: JsonValue };
  if (typeof obj["text"] !== "string") {
    return Err({ kind: "StructuralMismatch", path: `${path}.text`, expected: "string", actual: typeof obj["text"] });
  }
  const __text = obj["text"];
  return Ok({ text: __text } as ClientFrame);
}

export function serialise_RoomId(value: RoomId): JsonValue {
  return value as unknown as string;
}

export function deserialise_RoomId(json: JsonValue, path: string = "$"): Result<RoomId, BoundaryError> {
  if (typeof json !== "string") {
    return Err({ kind: "StructuralMismatch", path, expected: "string", actual: typeof json });
  }
  const validated = (typeof (RoomId as any).of === "function")
    ? (RoomId as any).of(json)
    : Ok(json as unknown as RoomId);
  if (validated.tag === "Err") {
    return Err({ kind: "RefinementViolation", path, violation: validated.error });
  }
  return Ok(validated.value as RoomId);
}

export function serialise_UserId(value: UserId): JsonValue {
  return value as unknown as string;
}

export function deserialise_UserId(json: JsonValue, path: string = "$"): Result<UserId, BoundaryError> {
  if (typeof json !== "string") {
    return Err({ kind: "StructuralMismatch", path, expected: "string", actual: typeof json });
  }
  const validated = (typeof (UserId as any).of === "function")
    ? (UserId as any).of(json)
    : Ok(json as unknown as UserId);
  if (validated.tag === "Err") {
    return Err({ kind: "RefinementViolation", path, violation: validated.error });
  }
  return Ok(validated.value as UserId);
}


