from server.Files import read_file
from server.GameBoard import GameBoard
from server.SnakeBuilder import SnakeBuilder

from server.storage.StorageLoader import StorageLoader

from flask import Flask
from flask import request
import logging, json, os, re

class Server:
  default_snake_config = {"apiversion":"1","author":"","color":"#888888","head":"default","tail":"default"}

  def __init__(self, data_path:str, snake_type:str, storage_type:str, debug:bool=False, store_game_when_win_and_moves_are_bigger_as:int=10):
    self.debug = debug
    self.snake_type = snake_type
    self.storage_type = storage_type

    self.config_file = os.path.join(data_path, 'data', 'snake-config.json')
    self.data_path = data_path

    self.store_game_state = False
    self.store_game_when_win_and_moves_are_bigger_as = store_game_when_win_and_moves_are_bigger_as

    self.running_games:dict[str, GameBoard] = {}

    self.app = Flask("Battlesnake")

    @self.app.get("/")
    def on_info():
      return self._info()

    @self.app.post("/start")
    def on_start():
      game_state = request.get_json()
      self._start(game_state)
      return "ok"

    @self.app.post("/move")
    def on_move():
      game_state = request.get_json()
      return self._move(game_state)

    @self.app.post("/end")
    def on_end():
      game_state = request.get_json()
      self._end(game_state)
      return "ok"

    @self.app.after_request
    def identify_server(response):
      response.headers.set(
        "server", "battlesnake/github/starter-snake-python"
      )
      return response

  def run(self, host:str="0.0.0.0", port:str="8000", debug:bool=False):
    logging.getLogger("werkzeug").setLevel(logging.ERROR)

    print(f"\nRunning Battlesnake at http://{host}:{port} with the {' '.join(re.findall('[A-Z][^A-Z]*', self.snake_type))}")
    self.app.run(host=host, port=port, debug=debug)

  def _read_json_config_or_create(self):
    snake_config = read_file(self.config_file, json.load)
    if not snake_config:
      snake_config = self._override_snake_config_with_environment_variables(self.default_snake_config)

    return self._override_snake_config_with_environment_variables(snake_config)

  def _override_snake_config_with_environment_variables(self, config:dict[str]):
    for key in ["author", "color", "head", "tail"]:
      if os.environ.get(f"SNAKE_{key.upper()}", None):
        config[key.lower()] = os.environ.get(f"SNAKE_{key.upper()}")
    return config

  def _create_game_board(self, game_state:dict):
    new_game_board = GameBoard(
      game_id=game_state["game"]["id"],
      width=game_state['board']['width'],
      height=game_state['board']['height'],
      ruleset=game_state['game']["ruleset"],
      source=game_state['game']['source'],
      map=game_state['game']['map'],
      snake_class=SnakeBuilder.build(self.snake_type)
    )
    new_game_board.start_game(game_state)

    self.running_games[game_state["game"]["id"]] = new_game_board
    return new_game_board

  def _delete_game_board(self, game_state):
    del self.running_games[game_state["game"]["id"]]

  def _get_game_board(self, game_state:str, end:bool=False):
    try:
      game_board = self.running_games[game_state["game"]["id"]]
    except KeyError:
      game_board = self._create_game_board(game_state)

    game_board.read_game_data(game_state)
    if end:
      game_board.end_game(game_state)

    return game_board

  def enable_store_game_state(self):
    self.store_game_state = True

  # info is called when you create your Battlesnake on play.battlesnake.com
  # and controls your Battlesnake's appearance
  # TIP: If you open your Battlesnake URL in a browser you should see this data
  def _info(self) -> dict:
    snake_config = self._read_json_config_or_create()

    print("INFO Snake:", snake_config)
    return snake_config

  # start is called when your Battlesnake begins a game
  def _start(self, game_state:dict):
    self._create_game_board(game_state)
    print("GAME START:", game_state["game"])

  # move is called when your Battlesnake game is running game
  def _move(self, game_state:dict) -> dict:
    game_board = self._get_game_board(game_state)
    next_move = game_board.snake_neat_make_a_move()

    if self.debug:
      print("TURN:", f'{game_state["turn"]:3},', "MOVE:", f"{next_move:5}")

    return {"move": next_move}

  # end is called when your Battlesnake finishes a game
  def _end(self, game_state:dict):
    if self.store_game_state:
      game_board = self._get_game_board(game_state, end=True)
      #if not game_board.get_winner() == "me" and not game_board.get_turn() <= self.store_game_when_win_and_moves_are_bigger_as:
      game_board.save(
        StorageLoader.build(self.storage_type),
        file_path=os.path.join(self.data_path, 'data'),
      )

    print("GAME ENDED: Winner is", [ x["name"] for x in game_state["board"]['snakes']])
    self._delete_game_board(game_state)
