"""Resource model for PROTOCOL - credits, attempts, and turn management.

Handles:
- Turn counter with proper increment logic
- Probe credits with regeneration
- Gate attempts (team-shared)
- Turn limit enforcement
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from purefn.games.protocol.engine import PROFILE_CONFIGS, Profile


@dataclass
class ResourceConfig:
    """Resource configuration derived from profile."""

    probe_cap: int
    probe_regen_every: int
    gate_attempts_default: int
    turn_limit: int


def get_resource_config(profile: Profile, turn_limit: int) -> ResourceConfig:
    """Get resource configuration for a profile.

    Args:
        profile: Difficulty profile
        turn_limit: Sampled turn limit (may vary for HARD)

    Returns:
        ResourceConfig with profile-specific settings
    """
    config = PROFILE_CONFIGS[profile]
    return ResourceConfig(
        probe_cap=config.probe_cap,
        probe_regen_every=config.probe_regen_every,
        gate_attempts_default=config.gate_attempts,
        turn_limit=turn_limit,
    )


def should_regenerate_credits(turn: int, regen_every: int) -> bool:
    """Check if credit regeneration should occur this turn.

    Regeneration occurs when turn % regen_every == 0 (after turn increment).

    Args:
        turn: Current turn number (after increment)
        regen_every: Regeneration interval

    Returns:
        True if regeneration should occur
    """
    return turn > 0 and turn % regen_every == 0


def apply_credit_regeneration(
    station: dict[str, Any],
    turn: int,
    regen_every: int,
) -> bool:
    """Apply credit regeneration to a station if applicable.

    Modifies station in-place if regeneration occurs.

    Args:
        station: Station dictionary (must have credits and cap)
        turn: Current turn number (after increment)
        regen_every: Regeneration interval

    Returns:
        True if regeneration was applied
    """
    if not should_regenerate_credits(turn, regen_every):
        return False

    credits = station.get("credits", 0)
    cap = station.get("cap", 0)

    if credits < cap:
        station["credits"] = credits + 1
        return True

    return False


def apply_all_credit_regeneration(
    stations: dict[str, dict[str, Any]],
    turn: int,
    regen_every: int,
) -> list[str]:
    """Apply credit regeneration to all lab stations.

    Args:
        stations: All stations dictionary
        turn: Current turn number (after increment)
        regen_every: Regeneration interval

    Returns:
        List of station IDs that had credits regenerated
    """
    regenerated = []
    for station_id, station in stations.items():
        station_type = station.get("type", "")
        if station_type.startswith("LAB_"):
            if apply_credit_regeneration(station, turn, regen_every):
                regenerated.append(station_id)
    return regenerated


def check_probe_credits(station: dict[str, Any]) -> tuple[bool, int]:
    """Check if a probe can be performed at a station.

    Args:
        station: Station dictionary

    Returns:
        Tuple of (can_probe, current_credits)
    """
    credits = station.get("credits", 0)
    return (credits > 0, credits)


def consume_probe_credit(station: dict[str, Any]) -> int:
    """Consume one probe credit from a station.

    Args:
        station: Station dictionary (modified in-place)

    Returns:
        Remaining credits after consumption

    Raises:
        ValueError: If no credits available
    """
    credits = int(station.get("credits", 0))
    if credits <= 0:
        raise ValueError("No probe credits available")
    station["credits"] = credits - 1
    return credits - 1


def check_gate_attempts(gate: dict[str, Any]) -> tuple[bool, int]:
    """Check if a gate response can be attempted.

    Args:
        gate: Gate station dictionary

    Returns:
        Tuple of (can_respond, attempts_remaining)
    """
    attempts = gate.get("attempts_remaining", 0)
    unlocked = gate.get("unlocked", False)
    return (attempts > 0 and not unlocked, attempts)


def consume_gate_attempt(gate: dict[str, Any]) -> int:
    """Consume one gate attempt.

    Args:
        gate: Gate station dictionary (modified in-place)

    Returns:
        Remaining attempts after consumption

    Raises:
        ValueError: If no attempts available or gate already unlocked
    """
    attempts = int(gate.get("attempts_remaining", 0))
    unlocked = bool(gate.get("unlocked", False))

    if unlocked:
        raise ValueError("Gate already unlocked")
    if attempts <= 0:
        raise ValueError("No gate attempts remaining")

    gate["attempts_remaining"] = attempts - 1
    return attempts - 1


def unlock_gate(gate: dict[str, Any]) -> None:
    """Unlock a gate after correct response.

    Args:
        gate: Gate station dictionary (modified in-place)
    """
    gate["unlocked"] = True


def check_turn_limit(turn: int, turn_limit: int) -> bool:
    """Check if turn limit has been reached.

    Args:
        turn: Current turn number
        turn_limit: Maximum turns allowed

    Returns:
        True if turn limit reached (expedition failed)
    """
    return turn >= turn_limit


def check_expedition_failed(state: dict[str, Any]) -> tuple[bool, str]:
    """Check if the expedition has failed.

    Failure conditions:
    - Turn limit reached
    - Any required gate permanently locked (attempts exhausted)

    Args:
        state: Full game state dictionary

    Returns:
        Tuple of (failed, reason)
    """
    turn = state.get("turn", 0)
    turn_limit = state.get("turn_limit", 80)

    if turn >= turn_limit:
        return (True, "Turn limit reached")

    # Check for permanently locked gates
    stations = state.get("stations", {})
    for station_id, station in stations.items():
        if station.get("type") == "GATE":
            attempts = station.get("attempts_remaining", 0)
            unlocked = station.get("unlocked", False)
            if not unlocked and attempts <= 0:
                return (True, f"Gate {station_id} permanently locked")

    return (False, "")


def check_expedition_success(state: dict[str, Any]) -> bool:
    """Check if the expedition has succeeded.

    Success: Any scout reaches GOAL with all gates unlocked.

    Args:
        state: Full game state dictionary

    Returns:
        True if expedition succeeded
    """
    # Check if any scout is at GOAL
    team = state.get("team", {})
    for scout_id, station_id in team.items():
        if station_id == "GOAL":
            # Verify all gates are unlocked
            gates_required = state.get("gates_required", 0)
            gates_unlocked = state.get("gates_unlocked", 0)
            if gates_unlocked >= gates_required:
                return True

    return False


def hamming_distance(a: int, b: int) -> int:
    """Compute Hamming distance between two bytes.

    Args:
        a: First byte (0-255)
        b: Second byte (0-255)

    Returns:
        Number of differing bits (0-8)
    """
    xor = a ^ b
    return bin(xor).count("1")
