import React from 'react'

import assertNever from '~/common/assertNever.ts'

import Loading from '../components/Loading.tsx'

import {
  AsyncHandle,
  Card,
  FighterArea,
  GameState,
  GameAction,
  GameCommandAPI,
} from './types.ts'
import useServerSocket from './useServerSocket.ts'
import Game from './Game.tsx'

interface GameClientState extends GameState {
  isDrawing: boolean
}

type GameClientAction = GameAction

function reducer(state: GameClientState, action: GameClientAction): GameClientState {
  switch (action.type) {
    case 'update-state': {
      return {...state, ...action.state}
    }
    case 'set-player-team': {
      return {...state, team: action.team}
    }
    case 'play-card': {
      const nextHand = [
        ...state.player.hand.slice(0, action.handIndex),
        ...state.player.hand.slice(action.handIndex + 1),
      ]
      return {
        ...state,
        player: { ...state.player, hand: nextHand },
      }
    }
    case 'receive-cards': {
      // first, i can draw and i am not yet drawing
      // scry N cards from the deck
      if (state.canDraw && !state.isDrawing) {
        return {
          ...state,
          isDrawing: true,
          drawChoices: action.cards,
        }
      }
      // then, i am drawing, i can still draw, and I haven't yet drawn.
      // i draw a card into my hand
      if (state.canDraw && state.isDrawing && !state.hasDrawn) {
        return {
          ...state,
          drawChoices: [],
          player: {
            ...state.player,
            hand: action.cards
          },
          isDrawing: false,
          hasDrawn: true,
        }
      }
      // then, i am no longer drawing and cannot draw. this is the board.
      const team = state.team === 1 ? 'sentinal' : 'scourge' as const
      return {
        ...state,
        board: {
          ...state.board,
          [team]: action.cards,
        },
      }
    }
    default: return state
  }
}

const initialState: GameClientState = {
	board: {
    sentinal: Array(4).fill(undefined) as FighterArea,
    scourge: Array(4).fill(undefined) as FighterArea,
  },
	player: {
    name: '',
    id: 0,
    hand: [],
    life: 0,
    ready: false,
  },
	deck: {
    cards: []
  },
  team: 1,
	enemyLife: 0,
	enemyDeckSize: 0,
	enemyHandSize: 0,
	currentTurn: 1,
	canDraw: false,
	hasDrawn: false,
	gameStatus: 0,
  isDrawing: false,
  drawChoices: [],
}

export default function GameClient(): JSX.Element {
  const [state, dispatch] = React.useReducer(reducer, initialState)

  const handleGameUpdate = React.useCallback((action: GameAction) => {
    dispatch(action)
  }, [])

  const socketHandle = useServerSocket(handleGameUpdate)

  const {team} = state
  const gameHandle = React.useMemo<AsyncHandle<GameCommandAPI>>(
    () => {
      if (socketHandle.status !== 'connected') return socketHandle
      return {
        status: 'connected',
        handle: {
          readyPlayer: () => {
            if (socketHandle.status !== 'connected') return
            socketHandle.handle.sendGameCommand({
              type: 's',
              cmd: 'b',
              player_id: team,
            })
          },
          startTurn: () => {
            if (socketHandle.status !== 'connected') return
            socketHandle.handle.sendGameCommand({
              type: 's',
              cmd: 's',
              player_id: team,
            })
          },
          endTurn: () => {
            if (socketHandle.status !== 'connected') return
            socketHandle.handle.sendGameCommand({
              type: 's',
              cmd: 'e',
              player_id: team,
            })
          },
          getView: () => {
            if (socketHandle.status !== 'connected') return
            socketHandle.handle.sendGameCommand({
              type: 's',
              cmd: 'g',
              player_id: team,
            })
          },
          startDraw: () => {
            if (socketHandle.status !== 'connected') return
            socketHandle.handle.sendGameCommand({
              type: 'a',
              cmd: 's',
              player_id: team,
            })
          },
          commitDraw: (cardIndex: number) => {
            if (socketHandle.status !== 'connected') return
            // dispatch({type})
            socketHandle.handle.sendGameCommand({
              type: 'a',
              cmd: `d ${cardIndex}`,
              player_id: team,
            })
          },
          playCard: (handIndex: number, positionIndex: number) => {
            if (socketHandle.status !== 'connected') return
            dispatch({ type: 'play-card', handIndex })
            socketHandle.handle.sendGameCommand({
              type: 'a',
              cmd: `p ${handIndex} ${positionIndex}`,
              player_id: team,
            })
          },
          moveCard: (positionFrom: number, positionTo: number) => {
            if (socketHandle.status !== 'connected') return
            socketHandle.handle.sendGameCommand({
              type: 'a',
              cmd: `m ${positionFrom} ${positionTo}`,
              player_id: team,
            })
          },
          attackCard: (positionFrom: number, positionTo: number) => {
            if (socketHandle.status !== 'connected') return
            socketHandle.handle.sendGameCommand({
              type: 'a',
              cmd: `a ${positionFrom} ${positionTo}`,
              player_id: team,
            })
          },
        },
      }
    },
    [socketHandle, team],
  )

  React.useEffect(() => {
    if (gameHandle.status !== 'connected') return
    gameHandle.handle.readyPlayer()
  }, [gameHandle, team])

  switch (gameHandle.status) {
    case 'connected': {
      return <Game {...state} {...gameHandle.handle} />
    }
    case 'not-connected':
    case 'connecting': {
      return <Loading />
    }
    default: return assertNever(gameHandle)
  }
}
