from __future__ import annotations

from dataclasses import dataclass, field
from typing import Dict, List, Optional, Set

from .data import (
    BUILD_COSTS,
    EdgeID,
    Resource,
    SETTLEMENT_LIMIT,
    CITY_LIMIT,
    ROAD_LIMIT,
    CornerCoord,
    DevelopmentCard,
)


def empty_resource_pool() -> Dict[Resource, int]:
    return {resource: 0 for resource in Resource if resource != Resource.DESERT}


@dataclass
class Player:
    name: str
    color: str
    resources: Dict[Resource, int] = field(default_factory=empty_resource_pool)
    roads: Set[EdgeID] = field(default_factory=set)
    settlements: Set[CornerCoord] = field(default_factory=set)
    cities: Set[CornerCoord] = field(default_factory=set)
    dev_cards: List[DevelopmentCard] = field(default_factory=list)
    new_dev_cards: List[DevelopmentCard] = field(default_factory=list)
    played_knights: int = 0
    hidden_points: int = 0
    largest_army: bool = False
    longest_road: bool = False

    def settlements_remaining(self) -> int:
        return SETTLEMENT_LIMIT - len(self.settlements)

    def cities_remaining(self) -> int:
        return CITY_LIMIT - len(self.cities)

    def roads_remaining(self) -> int:
        return ROAD_LIMIT - len(self.roads)

    def add_resources(self, payouts: Dict[Resource, int]) -> None:
        for resource, amount in payouts.items():
            if resource == Resource.DESERT:
                continue
            self.resources[resource] += amount

    def can_afford(self, cost_label: str) -> bool:
        cost = BUILD_COSTS[cost_label]
        return all(self.resources[res] >= amount for res, amount in cost.items())

    def pay_cost(self, cost_label: str) -> None:
        if not self.can_afford(cost_label):
            raise ValueError(f"{self.name} cannot afford {cost_label}")
        for resource, amount in BUILD_COSTS[cost_label].items():
            self.resources[resource] -= amount

    def total_resources(self) -> int:
        return sum(self.resources.values())

    def victory_points(self) -> int:
        points = len(self.settlements) + 2 * len(self.cities) + self.hidden_points
        if self.largest_army:
            points += 2
        if self.longest_road:
            points += 2
        return points

    def spend_resources(self, cards: Dict[Resource, int]) -> None:
        for resource, amount in cards.items():
            if self.resources[resource] < amount:
                raise ValueError("Insufficient resources")
        for resource, amount in cards.items():
            self.resources[resource] -= amount

    def receive_resource(self, resource: Resource, amount: int = 1) -> None:
        if resource != Resource.DESERT:
            self.resources[resource] += amount

    def lose_random_cards(self, amount: int, rng) -> Dict[Resource, int]:
        lost: Dict[Resource, int] = {res: 0 for res in self.resources}
        for _ in range(amount):
            available = [res for res, count in self.resources.items() for _ in range(count)]
            if not available:
                break
            res = rng.choice(available)
            self.resources[res] -= 1
            lost[res] += 1
        return lost
