from server.Files import read_file, save_file
from server.GameStorage import GameStorage
from snakes.TemplateSnake import TemplateSnake
from server.SnakeBuilder import SnakeBuilder

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

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

  def __init__(self, data_path:str, snake_type:str, debug:bool=False):
    self.debug = debug
    self.snake_type = snake_type

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

    self.store_game_state = False
    self.running_games:dict[str, GameStorage] = {}
    self.running_snake:dict[str, TemplateSnake] = {}

    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 {self.snake_type.replace('Snake', '')} Snake")
    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.default_snake_config
      save_file(self.config_file, snake_config, callback=json.dump, indent=2, ensure_ascii=False)
    return snake_config

  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):
    if self.store_game_state:
      self.running_games[game_state["game"]["id"]] = GameStorage(self.snake_type, path=os.path.join(self.data_path, 'data', 'history'))
      self.running_games[game_state["game"]["id"]].start_new_game(game_state["game"], game_state["board"], game_state["you"])

    self.running_snake[game_state["game"]["id"]] = SnakeBuilder.build(self.snake_type)
    print("GAME START:", game_state["game"])

  # move is called when your Battlesnake game is running game
  def _move(self, game_state:dict) -> dict:
    next_move = self.running_snake[game_state["game"]["id"]].choose_move(game_state)

    if self.store_game_state:
      self.running_games[game_state["game"]["id"]].add_moves(game_state["turn"], game_state["board"], next_move)
      if self.debug:
        print(self.running_games[game_state["game"]["id"]])

    print("TURN:", f'{game_state["turn"]:3},', "MOVE:", f"{next_move:5},", "Me:", {"head": game_state["you"]["head"], "length": game_state["you"]["length"]})
    return {"move": next_move}

  # end is called when your Battlesnake finishes a game
  def _end(self, game_state:dict):
    if self.store_game_state:
      snake = self.running_snake[game_state["game"]["id"]]

      self.running_games[game_state["game"]["id"]].add_end_state(game_state["board"], snake.get_history(), game_state["turn"])
      self.running_games[game_state["game"]["id"]].save(
        f"{snake.__class__.__name__}_{datetime.now().strftime('%H-%M-%S')}_{game_state['game']['id']}.json",
        callback=json.dump, indent=2, ensure_ascii=False
      )
      del self.running_games[game_state["game"]["id"]]

    print("GAME OVER:\n- Winner is", [ x["name"] for x in game_state["board"]['snakes']])
    del self.running_snake[game_state["game"]["id"]]
