"""Implementation of Wordle"""
import requests

from PIL import Image, ImageDraw

from .game_base import DatabaseGame, BaseDrawer

from gwendolyn_old.exceptions import GameNotInDatabase

IMAGE_MARGIN = 90
SQUARE_SIZE = 150
SQUARE_PADDING = 25
ROW_PADDING = 30
FONT_SIZE = 130

# COLORS
BACKGROUND_COLOR = "#2c2f33"
TEXT_COLOR = "#eee"
WRITING_BORDER_COLOR = "#999"
INACTIVE_BORDER_COLOR = "#555"
INCORRECT_COLOR = "#444"
CORRECT_COLOR = "#0a8c20"
PRESENT_COLOR = "#0f7aa8"

COLORS = [INCORRECT_COLOR, PRESENT_COLOR, CORRECT_COLOR]

class WordleGame(DatabaseGame):
  """Implementation of wordle game."""
  def __init__(self, bot):
    super().__init__(bot, "wordle", WordleDrawer)
    self._API_url = "https://api.wordnik.com/v4/words.json/randomWords?" # pylint: disable=invalid-name
    api_key = self.bot.credentials["wordnik_key"]
    self._APIPARAMS = { # pylint: disable=invalid-name
      "hasDictionaryDef": True,
      "minCorpusCount": 5000,
      "maxCorpusCount": -1,
      "minDictionaryCount": 1,
      "maxDictionaryCount": -1,
      "limit": 1,
      "api_key": api_key
    }

  async def start(self, ctx, letters: int):
    if self._test_document(str(ctx.channel_id)):
      await ctx.send("There is already a wordle game in this channel.")
      self.bot.log("There was already a game going on")
      return

    params = self._APIPARAMS
    params["minLength"] = letters
    params["maxLength"] = letters
    word = "-"
    while "-" in word or "." in word:
      response = requests.get(self._API_url, params=params)
      if response.json() == []:
        ctx.send("Could not find a word. Try again")
        return
      word = list(response.json()[0]["word"].upper())

    self.bot.log(f"Found the word \"{''.join(word)}\"")
    game = {"word": word, "guesses": [], "results": []}
    await ctx.send("Starting a wordle game.")
    await self._start_new(ctx.channel, game, delete=False)

  def _get_result(self, guess: list[str], word: list[str]):
    result = ["_" for _ in guess]
    for i, letter in enumerate(guess):
      if letter == word[i]:
        result[i] = "*"

    for i, letter in enumerate(guess):
      if letter in word and result[i] != "*":
        same_letter = guess[:i].count(letter)
        same_letter += len(
          [
            l for j,l in
            enumerate(guess[i:])
            if guess[i:][j] == letter and result[i:][j] == "*"
          ]
        )
        if same_letter < word.count(letter):
          result[i] = "-"

    return result

  async def guess(self, ctx, guess: str):
    if not guess.isalpha():
      await ctx.send("You can only use letters in your guess")
      return

    guess = list(guess.upper())
    try:
      game = self.access_document(str(ctx.channel_id))
    except GameNotInDatabase:
      await ctx.send("No game in channel")
      return

    if len(guess) != len(game['word']):
      await ctx.send(
        f"Your guess must be {len(game['word'])} letters long")
      return

    await ctx.send(f"Guessed {''.join(guess)}")
    result = self._get_result(guess, game['word'])

    updater = {
      "$set": {
        f"guesses.{len(game['guesses'])}": guess,
        f"results.{len(game['guesses'])}": result
      }
    }
    self._update_document(str(ctx.channel_id), updater)

    if result == ["*" for _ in game['word']]:
      await ctx.send("You guessed the word! Adding 15 GwendoBucks to your account")
      self.bot.money.addMoney(f"#{ctx.author_id}", 15)
      await self._end_game(ctx.channel)
    elif len(game['guesses']) == 5:
      await ctx.send(f"You used up all available guesses. The word was '{''.join(game['word'])}'")
      await self._end_game(ctx.channel)
    else:
      print(len(game['guesses']))
      await self._delete_old_image(ctx.channel)
      await self._send_image(ctx.channel, delete=False)

class WordleDrawer(BaseDrawer):
  def __init__(self, bot, game: WordleGame):
    super().__init__(bot, game)
    self.default_image = None
    self.default_color = BACKGROUND_COLOR
    self.default_size = (0, 0)

  def _get_size(self, game: dict):
    width = (
      (len(game['word']) * SQUARE_SIZE) +
      ((len(game['word']) - 1) * SQUARE_PADDING) +
      (2 * IMAGE_MARGIN)
    )
    height = (
      (6 * SQUARE_SIZE) +
      (5 * ROW_PADDING) +
      (2 * IMAGE_MARGIN)
    )
    size = (width, height)

    return size

  def _draw_row(self, row, drawer, font, word: str, colors: list[str] = None,
          border_color: str = None):
    if colors is None:
      colors = [BACKGROUND_COLOR for _ in range(len(word))]

    for i, letter in enumerate(word):
      y_pos = IMAGE_MARGIN + (row * (SQUARE_SIZE + ROW_PADDING))
      x_pos = IMAGE_MARGIN + (i * (SQUARE_SIZE + SQUARE_PADDING))
      top_left = (x_pos, y_pos)
      bottom_right = (x_pos + SQUARE_SIZE, y_pos + SQUARE_SIZE)

      drawer.rounded_rectangle(
        (top_left,bottom_right),
        10,
        colors[i],
        border_color,
        3
      )

      text_pos = (
        x_pos + (SQUARE_SIZE//2),
        y_pos + int(SQUARE_SIZE * 0.6)
      )

      drawer.text(text_pos, letter, TEXT_COLOR, font=font, anchor="mm")

  def _determine_colors(self, results):
    return [COLORS[["_","-","*"].index(symbol)] for symbol in results]

  def _draw_image(self, game: dict, image: Image.Image):
    drawer = ImageDraw.Draw(image)
    font = self._get_font(FONT_SIZE)
    for i, guess in enumerate(game['guesses']):
      colors = self._determine_colors(game['results'][i])
      self._draw_row(i, drawer, font, guess, colors)

    if len(game["guesses"]) < 6:
      self._draw_row(
        len(game['guesses']),
        drawer,
        font,
        " "*len(game['word']),
        border_color=WRITING_BORDER_COLOR
      )

    for i in range(5 - len(game['guesses'])):
      self._draw_row(
        len(game['guesses']) + i + 1,
        drawer,
        font, " "*len(game['word']),
        border_color=INACTIVE_BORDER_COLOR
      )

    return super()._draw_image(game, image)
