import { GameServerClient } from '@server/lib/client';
import { world } from '@game/struct/world';
import type { Coordinate, CoordinateKey, Hash, MapSegmentLike } from '@map/types';
import type { GameClientClientSocket, GameClientParams } from '@server/types';
import { isProduction } from '@core/const/env';
import { shouldUseMsgPack } from '@game/const';
import msgpack from 'socket.io-msgpack-parser';
import { io } from 'socket.io-client';
import { MapSegment } from '@map/lib/map/map-segment';
import { MapUpdate } from '@map/lib/map/map-update';
import { MapBuffer } from '@map/lib/map/map-buffer';
import type { TransferableArrayBuffer } from '@core/lib/base/normalize-buffer';

export let server: GameServer;

const params: GameClientParams = {
  type: 'client',
  token: 'fa0d4fe08402b31b3a836f0801ad84992b15dd2a8c3dcfe4c32449a35e3b2a9d'
};

export class GameServer extends GameServerClient {
  static connect(serverUrl: string) {
    const hasMsgPack = isProduction || shouldUseMsgPack;
    const parser = hasMsgPack ? msgpack : undefined;

    const socket: GameClientClientSocket = io(serverUrl, {
      transports: ['websocket'],
      parser,
      reconnectionDelay: 100,
      reconnectionDelayMax: 2000,
      auth: params
    });

    return (server = new GameServer(socket));
  }

  onConnect() {
    super.onConnect();

    if (world.visibleArea) {
      world.updateVisibleArea(world.visibleArea, true);
    }
  }

  protected onSyncChunks(objects: MapSegmentLike[], chunksToValidate?: Array<[Coordinate, Hash]>) {
    super.onSyncChunks(objects, chunksToValidate);

    const chunksToInsert = MapSegment.fromObjects(objects);

    const updated = world.mergeAll(chunksToInsert);

    if (chunksToValidate) {
      world.validateAndUpdateChunks(chunksToValidate);
    }

    if (updated) {
      // previewMap(world);
    }
  }

  protected onUpdateChunk(chunkKey: CoordinateKey, buffer: TransferableArrayBuffer) {
    super.onUpdateChunk(chunkKey, buffer);

    const update = new MapUpdate(world.scale, world.offset, MapBuffer.toMapBuffer(buffer));

    world.apply(update);
  }
}
