/**
 * REST client for auth, lobbies and cloud saves (GDD §9).
 *
 * The token lives in memory rather than localStorage: an XSS in a game that
 * loads user-generated content later should not hand over the session too.
 */

export interface PublicUser {
  id: string;
  username: string;
  displayName: string | null;
}

export interface LobbyPlayer {
  peerId: number;
  displayName: string;
  isReady: boolean;
  isHost: boolean;
}

export interface Lobby {
  code: string;
  status: "waiting" | "starting" | "in_game" | "closed";
  maxPlayers: number;
  players: LobbyPlayer[];
}

export class ApiError extends Error {
  constructor(
    message: string,
    readonly status: number,
  ) {
    super(message);
    this.name = "ApiError";
  }
}

export class ApiClient {
  private token: string | null = null;
  user: PublicUser | null = null;

  constructor(readonly baseUrl = import.meta.env["VITE_API_URL"] ?? "http://localhost:8787") {}

  get isAuthenticated(): boolean {
    return this.token !== null;
  }

  get sessionToken(): string | null {
    return this.token;
  }

  private async request<T>(path: string, init: RequestInit = {}): Promise<T> {
    const headers: Record<string, string> = {
      "content-type": "application/json",
      ...((init.headers as Record<string, string>) ?? {}),
    };
    if (this.token) headers["authorization"] = `Bearer ${this.token}`;

    const response = await fetch(`${this.baseUrl}${path}`, { ...init, headers });
    const body = (await response.json().catch(() => ({}))) as Record<string, unknown>;

    if (!response.ok) {
      throw new ApiError(
        typeof body["error"] === "string" ? body["error"] : response.statusText,
        response.status,
      );
    }
    return body as T;
  }

  async register(username: string, email: string, password: string): Promise<PublicUser> {
    const result = await this.request<{ token: string; user: PublicUser }>("/auth/register", {
      method: "POST",
      body: JSON.stringify({ username, email, password }),
    });
    this.token = result.token;
    this.user = result.user;
    return result.user;
  }

  async login(username: string, password: string): Promise<PublicUser> {
    const result = await this.request<{ token: string; user: PublicUser }>("/auth/login", {
      method: "POST",
      body: JSON.stringify({ username, password }),
    });
    this.token = result.token;
    this.user = result.user;
    return result.user;
  }

  createLobby(): Promise<Lobby> {
    return this.request<Lobby>("/lobbies", { method: "POST", body: "{}" });
  }

  joinLobby(code: string): Promise<Lobby> {
    return this.request<Lobby>(`/lobbies/${code.toUpperCase()}/join`, { method: "POST" });
  }

  getLobby(code: string): Promise<Lobby> {
    return this.request<Lobby>(`/lobbies/${code.toUpperCase()}`);
  }

  setReady(code: string, isReady: boolean): Promise<Lobby> {
    return this.request<Lobby>(`/lobbies/${code.toUpperCase()}/ready`, {
      method: "POST",
      body: JSON.stringify({ isReady }),
    });
  }

  startLobby(code: string): Promise<Lobby> {
    return this.request<Lobby>(`/lobbies/${code.toUpperCase()}/start`, { method: "POST" });
  }

  leaveLobby(code: string): Promise<Lobby> {
    return this.request<Lobby>(`/lobbies/${code.toUpperCase()}/leave`, { method: "POST" });
  }

  signOut(): void {
    this.token = null;
    this.user = null;
  }
}
