from __future__ import annotations

from typing import Generator

from draughts.models import Figure


class Move:
    """Move representation.
    End user should never interact with this class directly.

    As we can read on wikipedia:

    *Multiple jumps, such as a double or triple jump,
    require you to pay attention, as the convention is
    to just show the start and end squares and not the
    in-between or intermediate squares. So the notation
    1-3 would mean a King does a double jump from 1 to 10 to 3.
    The intermediate square is only shown if there are two ways
    to jump and it would not be clear otherwise.*

    Note that always:
    n - number of visited squares (include source square)
    n - 2 - number of captured pieces
    """

    def __init__(
        self,
        visited_squares: list[int],
        captured_list: list[int] = [],
        captured_entities: list[Figure.value] = [],
        is_promotion: bool = False,
    ) -> None:
        self.square_list = visited_squares
        self.captured_list = captured_list
        self.captured_entities = captured_entities
        self.is_promotion = is_promotion

    def __repr__(self) -> str:
        visited_squares = [str(s + 1) for s in self.square_list]
        return f"Move: {'->'.join(visited_squares)}"

    def __eq__(self, other: object) -> bool:
        """Check if two moves are equal. move created from string will have only visited squares definied."""
        if not isinstance(other, Move):
            return False

        if (
            self.square_list[0] == other.square_list[0]
            and self.square_list[-1] == other.square_list[-1]
        ):
            longer = (
                self.square_list
                if len(self.square_list) >= len(other.square_list)
                else other.square_list
            )
            shorter = (
                self.square_list
                if len(self.square_list) < len(other.square_list)
                else other.square_list
            )

            return all(square in longer for square in shorter)

        return False

    def __len__(self) -> int:
        return len(self.captured_list) + 1

    def __add__(self, other: Move) -> Move:
        """Append moves"""
        if self.square_list[-1] != other.square_list[0]:
            raise ValueError(
                f"Cannot append moves {self} and {other}. Last square of first move should be equal to first square of second move."
            )
        return Move(
            self.square_list + other.square_list[1:],
            self.captured_list + other.captured_list,
            self.captured_entities + other.captured_entities,
        )

    @classmethod
    def from_string(cls, move: str, legal_moves: Generator) -> Move:
        """

        Converts string representation of move to ``Move`` object.
        This is generic method, so it can be used for any board size. Therefore,
        For different context different move object will be generated.
        Also we need to pass legal moves, to understand given move and check if it is legal.


        input format:

        * ``<square_number>-<square_number>`` for simple move
        * ``<square_number>x<square_number>`` for capture

        Examples:

        * ``24-19`` - means from 24 to 19
        * ``24x19`` - means from 24 to 16 means capture of piece between 24 and 16
        * ``1x10x19`` - means capture of two pieces between 1 and 19

        """
        move = move.lower()
        if "-" in move:  # classic move
            steps = move.split("-")
        elif "x" in move:  # this means capture
            steps = move.split("x")
        else:
            raise ValueError(f"Invalid move {move}.")

        move = Move([int(step) - 1 for step in steps])
        for legal_move in legal_moves:
            if legal_move == move:
                return legal_move
        raise ValueError(
            f"{move} is correct, but not legal in given position.\n Legal moves are: {list(legal_moves)}"
        )
