import type { SaeeolChatEventMap, SaeeolChatEventName } from "./types"

export const WS_SUBPROTOCOL = "saeeol.events.v1"
export const HANDSHAKE_TIMEOUT_MS = 10_000
export const PING_INTERVAL_MS = 15_000
export const TICKET_FETCH_TIMEOUT_MS = 10_000

export class WebSocketAuthError extends Error {
  constructor(message = "WebSocket authentication failed") {
    super(message)
    this.name = "WebSocketAuthError"
  }
}

export class WebSocketConnectError extends Error {
  constructor(
    message: string,
    public readonly code: number,
  ) {
    super(message)
    this.name = "WebSocketConnectError"
  }
}

export class HandshakeTimeoutError extends Error {
  constructor() {
    super("WebSocket handshake timed out")
    this.name = "HandshakeTimeoutError"
  }
}

export function isAuthCloseCode(code: number): boolean {
  if (code === 1008) return true
  if (code === 4401 || code === 4403) return true
  return false
}

export type EventHandler = (context: string, payload: unknown) => void

export type EventServiceConfig = {
  url: string
  getToken: () => Promise<string>
  onUnauthorized?: () => void
}

export function toHttpBase(wsBase: string): string {
  const trimmed = wsBase.replace(/\/$/, "")
  if (trimmed.startsWith("wss://")) return "https://" + trimmed.slice(6)
  if (trimmed.startsWith("ws://")) return "http://" + trimmed.slice(5)
  return trimmed
}

export async function fetchTicket(wsBaseUrl: string, token: string): Promise<string> {
  const ctrl = new AbortController()
  const timer = setTimeout(() => ctrl.abort(), TICKET_FETCH_TIMEOUT_MS)
  try {
    const res = await fetch(toHttpBase(wsBaseUrl) + "/connect-ticket", {
      method: "POST",
      headers: { Authorization: `Bearer ${token}` },
      signal: ctrl.signal,
    })
    if (res.status === 401 || res.status === 403) {
      throw new WebSocketAuthError(`Event-service rejected ticket request: ${res.status}`)
    }
    if (!res.ok) {
      throw new WebSocketConnectError(`Failed to mint event-service ticket: ${res.status}`, res.status)
    }
    const body = (await res.json().catch(() => null)) as { ticket?: unknown } | null
    if (!body || typeof body.ticket !== "string" || !body.ticket) {
      throw new WebSocketConnectError("Malformed event-service ticket response", 0)
    }
    return body.ticket
  } catch (err) {
    if (err instanceof WebSocketAuthError || err instanceof WebSocketConnectError) throw err
    if ((err as { name?: string })?.name === "AbortError") {
      throw new HandshakeTimeoutError()
    }
    throw new WebSocketConnectError(`Event-service ticket request failed: ${(err as Error)?.message ?? err}`, 0)
  } finally {
    clearTimeout(timer)
  }
}

export function parseMessage(data: string, handlers: Map<string, Set<EventHandler>>): void {
  if (data === "pong") return
  let parsed: unknown
  try {
    parsed = JSON.parse(data)
  } catch {
    return
  }
  if (!parsed || typeof parsed !== "object") return
  const m = parsed as Record<string, unknown>
  if (m.type === "event" && typeof m.context === "string" && typeof m.event === "string") {
    const h = handlers.get(m.event)
    if (h) {
      for (const handler of h) handler(m.context, m.payload)
    }
    return
  }
  if (m.type === "error") {
    console.warn("[Saeeol] event-service server error", m)
  }
}
