<template>
  <el-main class="!flex bg-white">
    <el-container>
      <gobang-header class="justify-between">
        <div class="flex items-center">
          <el-button type="primary" :loading="loading" @click="refresh">刷新</el-button>
          <el-button type="success" :disabled="loading" @click="showDialog = true">
            创建房间
          </el-button>
          <el-button type="primary" disabled>单人游戏</el-button>
        </div>
      </gobang-header>
      <el-main
        v-loading="loading"
        :element-loading-text="loadingText"
        class="gobang-list-page-main flex-col"
        :class="[!rooms.length ? 'justify-center items-center' : '', { '!flex': !rooms.length }]"
      >
        <template v-if="rooms.length || !loading">
          <template v-if="!rooms.length">
            <div class="gobang-list-page-main__no-rooms-title">没有房间</div>
            <el-button type="primary" :loading="loading" @click="refresh">刷新</el-button>
          </template>
          <el-table v-else :data="rooms">
            <el-table-column prop="briefId" label="房间ID" />
            <el-table-column prop="briefId" :formatter="getPlayerCount" label="房间人数" />
            <el-table-column label="操作">
              <template #default="{ row }">
                <el-button
                  type="success"
                  :disabled="
                    arrayIncludes([RoomState.GAMING, RoomState.FINISHED], (row as RoomRender).state)
                  "
                  @click="onJoinButtonClick(row)"
                >
                  <template
                    v-if="
                      arrayIncludes(
                        [RoomState.GAMING, RoomState.FINISHED],
                        (row as RoomRender).state,
                      )
                    "
                  >
                    已满
                  </template>
                  <template v-else>加入</template>
                </el-button>
              </template>
            </el-table-column>
          </el-table>
        </template>
      </el-main>
    </el-container>
    <create-gobang-room-dialog v-model="showDialog" @click="createRoom" />
  </el-main>
</template>
<style lang="scss" scoped>
.gobang-list-page-header__title {
  font-size: 25px;
  font-weight: bold;
}
.gobang-list-page-main {
  --el-main-padding: 0 0 0 0;
  margin-top: var(--header-height);
  gap: 10px;
  &__no-rooms-title {
    font-size: 25px;
    font-weight: bold;
  }
}
</style>
<script lang="ts">
/** 房间状态 */
export enum RoomState {
  /** 房间无人 */
  CREATED = 'CREATED',
  /** 房间等人 */
  WAITING = 'WAITING',
  /** 正在游戏 */
  GAMING = 'GAMING',
  /** 正在游戏，但对局已经结束 */
  FINISHED = 'FINISHED',
}
/** 房间UUID */
export type RoomId = string;
export type Room = {
  id: RoomId;
  full: boolean;
  state: RoomState;
  isWhiteAcceptRestart: boolean;
  isBlackAcceptRestart: boolean;
  canWhiteDown: boolean;
};
export type RoomDetail = {
  roomId: RoomId;
  state: RoomState;
  pieces: (-1 | 0 | 1)[][];
  whiteUser?: UserInfo;
  blackUser?: UserInfo;
  blackRequestRestart?: boolean;
  whiteRequestRestart?: boolean;
  canWhiteDown: boolean;
};
export enum WinFace {
  UP = 'UP',
  UP_RIGHT = 'UP_RIGHT',
  RIGHT = 'RIGHT',
  DOWN_RIGHT = 'DOWN_RIGHT',
  DOWN = 'DOWN',
  DOWN_LEFT = 'DOWN_LEFT',
  LEFT = 'LEFT',
  UP_LEFT = 'UP_LEFT',
}
export type Request =
  | SimplePart<'RoomList'>
  | SimplePart<'CreateRoom'>
  | PayloadPart<'PlayerJoin', { roomId: RoomId }>
  | SimplePart<'ResetRoom'>
  | PayloadPart<'PlaceChessPiece', { x: number; y: number }>;
export type Resp =
  | PayloadPart<'UserInfo', UserInfo>
  | PayloadPart<'RoomList', { rooms: Room[] }>
  | PayloadPart<'RoomCreated', { roomId: RoomId }>
  | PayloadPart<'PlayerSideAllocation', { isWhite: boolean }>
  | PayloadPart<'RoomInfo', RoomDetail>
  | PayloadPart<'PlayerJoinRoom', { room: Room; user: UserInfo }>
  | PayloadPart<'PlayerLeave'>
  | PayloadPart<
      'HasPlayerWin',
      {
        winInfo: {
          face: WinFace;
          isWhite: boolean;
          originalX: number;
          originalY: number;
        };
      }
    >
  | PayloadPart<'PlayerWin'>
  | PayloadPart<'PlayerLose'>;
const relations = {
  RoomList: 'RoomList',
  CreateRoom: 'RoomCreated',
  PlayerJoin: ['PlayerSideAllocation', 'RoomInfo'],
  PlaceChessPiece: ['RoomInfo'],
  ResetRoom: ['RoomInfo'],
} as const satisfies Relations<Request['name'], Resp['name']>;
export function useGobangSocket(
  options: Omit<UseGameSocketOptions<Request, Resp, typeof relations>, 'url' | 'relations'>,
) {
  return useGameSocket<Request, Resp>()(
    Object.assign(options, {
      url: `${WEBSOCKET_BASE_URL}/chess/${useUserStore().token}`,
      relations,
    }),
  );
}
const playerCountMap: Record<RoomState, string> = {
  [RoomState.CREATED]: '0/2',
  [RoomState.WAITING]: '1/2',
  [RoomState.GAMING]: '2/2',
  [RoomState.FINISHED]: '2/2',
};
</script>
<script lang="ts" setup>
import CreateGobangRoomDialog from '@/components/gobang/CreateGobangRoomDialog.vue';
import GobangHeader from '@/components/gobang/GobangHeader.vue';
import { WEBSOCKET_BASE_URL } from '@/env';
import router from '@/router';
import type { UserInfo } from '@/schemas/response';
import { useUserStore } from '@/stores/user';
import { arrayIncludes } from '@/utils/array';
import {
  useGameSocket,
  type PayloadPart,
  type Relations,
  type SimplePart,
  type UseGameSocketOptions,
} from '@/utils/game-socket';
import { onMounted, ref } from 'vue';
type RoomRender = Room & { briefId: string };
function toRoomRender(room: Room): RoomRender {
  return Object.assign(room, {
    briefId: room.id.slice(0, 8),
  });
}
const showDialog = ref(false);
const rooms = ref<RoomRender[]>([]);
const loading = ref(false);
const loadingText = ref<string>();
const { send } = useGobangSocket({
  succeed: {
    RoomList(p) {
      rooms.value = p.rooms.map(toRoomRender);
    },
    RoomCreated(p) {
      play(p.roomId);
    },
  },
  finally: {
    RoomList() {
      loading.value = false;
    },
  },
});
function refresh() {
  loading.value = true;
  loadingText.value = '加载中';
  send({ name: 'RoomList' });
}
onMounted(() => {
  refresh();
});
function getPlayerCount(row: RoomRender) {
  return playerCountMap[row.state];
}
function onJoinButtonClick(room: RoomRender) {
  play(room.id);
}
function play(roomId: RoomId) {
  router.push({
    name: 'GobangPlayMulti',
    params: { id: roomId },
  });
}
function createRoom() {
  send({ name: 'CreateRoom' });
}
</script>
