defmodule Chessh.SSH.Client.PreviousGame do
  @start_fen "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"

  alias Chessh.{Game, Utils}
  alias Chessh.SSH.Client.Game.Renderer
  alias IO.ANSI

  require Logger

  defmodule State do
    defstruct move_fens: %{},
              move_idx: 0,
              binbo_pid: nil,
              game: %Game{},
              client_pid: nil,
              flipped: false,
              viewing_uci: false
  end

  use Chessh.SSH.Client.Screen

  def init([
        %State{
          client_pid: client_pid,
          game: %Game{
            game_moves: game_moves
          }
        } = state
      ]) do
    {:ok, binbo_pid} = :binbo.new_server()
    :binbo.new_game(binbo_pid, @start_fen)

    {move_fens, _moves} =
      game_moves
      |> String.trim()
      |> String.split(" ")
      |> Enum.reduce({%{"0" => @start_fen}, 1}, fn move, {move_idx_fen_map, curr_turn} ->
        {:ok, _status} = :binbo.move(binbo_pid, move)
        {:ok, fen} = :binbo.get_fen(binbo_pid)

        {Map.put(move_idx_fen_map, "#{curr_turn}", fen), curr_turn + 1}
      end)

    new_state = %State{
      state
      | binbo_pid: binbo_pid,
        move_fens: move_fens
    }

    send(client_pid, {:send_to_ssh, Utils.clear_codes()})
    render(new_state)

    {:ok, new_state}
  end

  def input(
        _width,
        _height,
        action,
        %State{
          move_idx: move_idx,
          flipped: flipped,
          viewing_uci: viewing_uci,
          game: %Game{
            moves: num_moves
          }
        } = state
      ) do
    new_move_idx =
      case action do
        :left ->
          Utils.wrap_around(move_idx, -1, num_moves + 1)

        :right ->
          Utils.wrap_around(move_idx, 1, num_moves + 1)

        _ ->
          move_idx
      end

    new_state = %State{
      state
      | move_idx: new_move_idx,
        flipped: if(action == "f", do: !flipped, else: flipped),
        viewing_uci: if(action == "m", do: !viewing_uci, else: viewing_uci)
    }

    render(new_state)
    new_state
  end

  def render(
        %State{
          flipped: flipped,
          client_pid: client_pid,
          move_fens: move_fens,
          move_idx: move_idx,
          game: %Game{id: game_id, moves: total_moves, game_moves: game_moves},
          viewing_uci: viewing_uci
        } = state
      ) do
    lines =
      case viewing_uci do
        false ->
          {:ok, fen} = Map.fetch(move_fens, "#{move_idx}")

          [
            "Game #{game_id} | Move #{move_idx} / #{total_moves}",
            "| <- previous move | next move ->",
            "| press 'm' to view move history",
            "==="
          ] ++
            Renderer.draw_board(fen, flipped)

        true ->
          [
            Utils.clear_codes(),
            "UCI Notation For Game #{game_id}",
            "- Press 'm' to go back to the board",
            "- Use https://dcode.fr/uci-chess-notation to convert to PGN",
            "",
            game_moves
          ]
      end

    send(
      client_pid,
      {:send_to_ssh,
       [ANSI.home()] ++
         Enum.map(
           Enum.zip(1..length(lines), lines),
           fn {i, line} ->
             [ANSI.cursor(i, 0), ANSI.clear_line(), line]
           end
         ) ++ [ANSI.home()]}
    )

    state
  end

  def render(_width, _height, %State{} = state), do: render(state)
end
