"""
Configuration management for Circuit Relay v2.

This module handles configuration for relay roles, resource limits,
and discovery settings.
"""

from dataclasses import (
    dataclass,
    field,
)
from enum import Flag, auto

from nanolib.peer.peerinfo import (
    PeerInfo,
)

from .resources import (
    RelayLimits,
)

DEFAULT_MIN_RELAYS = 3
DEFAULT_MAX_RELAYS = 20
DEFAULT_DISCOVERY_INTERVAL = 300  # seconds
DEFAULT_RESERVATION_TTL = 3600  # seconds
DEFAULT_MAX_CIRCUIT_DURATION = 3600  # seconds
DEFAULT_MAX_CIRCUIT_BYTES = 1024 * 1024 * 1024  # 1GB

DEFAULT_MAX_CIRCUIT_CONNS = 8
DEFAULT_MAX_RESERVATIONS = 4

MAX_RESERVATIONS_PER_IP = 8
MAX_CIRCUITS_PER_IP = 16
RESERVATION_RATE_PER_IP = 4  # per minute
CIRCUIT_RATE_PER_IP = 8  # per minute
MAX_CIRCUITS_TOTAL = 64
MAX_RESERVATIONS_TOTAL = 32
MAX_BANDWIDTH_PER_CIRCUIT = 1024 * 1024  # 1MB/s
MAX_BANDWIDTH_TOTAL = 10 * 1024 * 1024  # 10MB/s

MIN_RELAY_SCORE = 0.5
MAX_RELAY_LATENCY = 1.0  # seconds
ENABLE_AUTO_RELAY = True
AUTO_RELAY_TIMEOUT = 30  # seconds
MAX_AUTO_RELAY_ATTEMPTS = 3
RESERVATION_REFRESH_THRESHOLD = 0.8  # Refresh at 80% of TTL
MAX_CONCURRENT_RESERVATIONS = 2

# Timeout constants for different components
DEFAULT_DISCOVERY_STREAM_TIMEOUT = 10  # seconds
DEFAULT_PEER_PROTOCOL_TIMEOUT = 5  # seconds
DEFAULT_PROTOCOL_READ_TIMEOUT = 15  # seconds
DEFAULT_PROTOCOL_WRITE_TIMEOUT = 15  # seconds
DEFAULT_PROTOCOL_CLOSE_TIMEOUT = 10  # seconds
DEFAULT_DCUTR_READ_TIMEOUT = 30  # seconds
DEFAULT_DCUTR_WRITE_TIMEOUT = 30  # seconds
DEFAULT_DIAL_TIMEOUT = 10  # seconds


@dataclass
class TimeoutConfig:
    """Timeout configuration for different Circuit Relay v2 components."""

    # Discovery timeouts
    discovery_stream_timeout: int = DEFAULT_DISCOVERY_STREAM_TIMEOUT
    peer_protocol_timeout: int = DEFAULT_PEER_PROTOCOL_TIMEOUT

    # Core protocol timeouts
    protocol_read_timeout: int = DEFAULT_PROTOCOL_READ_TIMEOUT
    protocol_write_timeout: int = DEFAULT_PROTOCOL_WRITE_TIMEOUT
    protocol_close_timeout: int = DEFAULT_PROTOCOL_CLOSE_TIMEOUT

    # DCUtR timeouts
    dcutr_read_timeout: int = DEFAULT_DCUTR_READ_TIMEOUT
    dcutr_write_timeout: int = DEFAULT_DCUTR_WRITE_TIMEOUT
    dial_timeout: int = DEFAULT_DIAL_TIMEOUT


# Relay roles enum
class RelayRole(Flag):
    """
    Bit-flag enum that captures the three possible relay capabilities.

    A node can combine multiple roles using bit-wise OR, for example::

        RelayRole.HOP | RelayRole.STOP
    """

    HOP = auto()  # Act as a relay for others ("hop")
    STOP = auto()  # Accept relayed connections ("stop")
    CLIENT = auto()  # Dial through existing relays ("client")


@dataclass
class RelayConfig:
    """Configuration for Circuit Relay v2."""

    # Role configuration (bit-flags)
    roles: RelayRole = RelayRole.STOP | RelayRole.CLIENT

    # Resource limits
    limits: RelayLimits | None = None

    # Discovery configuration
    bootstrap_relays: list[PeerInfo] = field(default_factory=list)
    min_relays: int = DEFAULT_MIN_RELAYS
    max_relays: int = DEFAULT_MAX_RELAYS
    discovery_interval: int = DEFAULT_DISCOVERY_INTERVAL

    # Connection configuration
    reservation_ttl: int = DEFAULT_RESERVATION_TTL
    max_circuit_duration: int = DEFAULT_MAX_CIRCUIT_DURATION
    max_circuit_bytes: int = DEFAULT_MAX_CIRCUIT_BYTES

    # Timeout configuration
    timeouts: TimeoutConfig = field(default_factory=TimeoutConfig)

    # ---------------------------------------------------------------------
    # Backwards-compat boolean helpers.  Existing code that still accesses
    # ``cfg.enable_hop, cfg.enable_stop, cfg.enable_client`` will continue to work.
    # ---------------------------------------------------------------------

    @property
    def enable_hop(self) -> bool:  # pragma: no cover – helper
        return bool(self.roles & RelayRole.HOP)

    @property
    def enable_stop(self) -> bool:  # pragma: no cover – helper
        return bool(self.roles & RelayRole.STOP)

    @property
    def enable_client(self) -> bool:  # pragma: no cover – helper
        return bool(self.roles & RelayRole.CLIENT)

    @property
    def enable_dht_discovery(self) -> bool:  # pragma: no cover - helper
        return False

    def __post_init__(self) -> None:
        """Initialize default values."""
        if self.limits is None:
            self.limits = RelayLimits(
                duration=self.max_circuit_duration,
                data=self.max_circuit_bytes,
                max_circuit_conns=DEFAULT_MAX_CIRCUIT_CONNS,
                max_reservations=DEFAULT_MAX_RESERVATIONS,
            )


@dataclass
class HopConfig:
    """Configuration specific to relay (hop) nodes."""

    # Resource limits per IP
    max_reservations_per_ip: int = MAX_RESERVATIONS_PER_IP
    max_circuits_per_ip: int = MAX_CIRCUITS_PER_IP

    # Rate limiting
    reservation_rate_per_ip: int = RESERVATION_RATE_PER_IP
    circuit_rate_per_ip: int = CIRCUIT_RATE_PER_IP

    # Resource quotas
    max_circuits_total: int = MAX_CIRCUITS_TOTAL
    max_reservations_total: int = MAX_RESERVATIONS_TOTAL

    # Bandwidth limits
    max_bandwidth_per_circuit: int = MAX_BANDWIDTH_PER_CIRCUIT
    max_bandwidth_total: int = MAX_BANDWIDTH_TOTAL


@dataclass
class ClientConfig:
    """Configuration specific to relay clients."""

    # Relay selection
    min_relay_score: float = MIN_RELAY_SCORE
    max_relay_latency: float = MAX_RELAY_LATENCY

    # Auto-relay settings
    enable_auto_relay: bool = ENABLE_AUTO_RELAY
    auto_relay_timeout: int = AUTO_RELAY_TIMEOUT
    max_auto_relay_attempts: int = MAX_AUTO_RELAY_ATTEMPTS

    # Reservation management
    reservation_refresh_threshold: float = RESERVATION_REFRESH_THRESHOLD
    max_concurrent_reservations: int = MAX_CONCURRENT_RESERVATIONS
