from dataclasses import dataclass
from typing import Iterable, Union, List, Tuple, Hashable, TYPE_CHECKING

from BaseClasses import CollectionState
from .base import BaseStardewRule, CombinableStardewRule
from .protocol import StardewRule
from ..strings.ap_names.event_names import Event

if TYPE_CHECKING:
    from .. import StardewValleyWorld


class TotalReceived(BaseStardewRule):
    count: int
    items: Iterable[str]
    player: int

    def __init__(self, count: int, items: Union[str, Iterable[str]], player: int):
        items_list: List[str]

        if isinstance(items, Iterable):
            items_list = [*items]
        else:
            items_list = [items]

        self.player = player
        self.items = items_list
        self.count = count

    def __call__(self, state: CollectionState) -> bool:
        c = 0
        for item in self.items:
            c += state.count(item, self.player)
            if c >= self.count:
                return True
        return False

    def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRule, bool]:
        return self, self(state)

    def __repr__(self):
        return f"Received {self.count} {self.items}"


@dataclass(frozen=True)
class Received(CombinableStardewRule):
    item: str
    player: int
    count: int
    event: bool = False
    """Helps `explain` to know it can dig into a location with the same name."""

    @property
    def combination_key(self) -> Hashable:
        return self.item

    @property
    def value(self):
        return self.count

    def __call__(self, state: CollectionState) -> bool:
        return state.has(self.item, self.player, self.count)

    def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRule, bool]:
        return self, self(state)

    def __repr__(self):
        if self.count == 1:
            return f"Received {'event ' if self.event else ''}{self.item}"
        return f"Received {'event ' if self.event else ''}{self.count} {self.item}"


@dataclass(frozen=True)
class Reach(BaseStardewRule):
    spot: str
    resolution_hint: str
    player: int

    def __call__(self, state: CollectionState) -> bool:
        if self.resolution_hint == 'Region' and self.spot not in state.multiworld.regions.region_cache[self.player]:
            return False
        return state.can_reach(self.spot, self.resolution_hint, self.player)

    def evaluate_while_simplifying(self, state: CollectionState) -> Tuple[StardewRule, bool]:
        return self, self(state)

    def __repr__(self):
        return f"Reach {self.resolution_hint} {self.spot}"


class HasProgressionPercent(Received):
    def __init__(self, player: int, percent: int):
        super().__init__(Event.received_progression_percent, player, percent, event=True)

    def __post_init__(self):
        assert self.count > 0, "HasProgressionPercent rule must be above 0%"
        assert self.count <= 100, "HasProgressionPercent rule can't require more than 100% of items"

    def __repr__(self):
        return f"Received {self.count}% progression items"
