"""
Configuration management for mongoengine read-write router.

Centralized settings for database routing behavior and consistency strategies.
"""

import random
from dataclasses import dataclass, field
from typing import Literal

from uhashring import HashRing


@dataclass
class MongoRouterConfig:
    """Configuration for mongoengine read-write router."""

    # Database aliases
    primary_alias: str = "default"
    replica_aliases: list[str] = field(default_factory=lambda: ["replica1", "replica2"])

    # Routing strategy
    strategy: Literal["random", "transaction_aware", "consistent_hash"] = "transaction_aware"

    # Hash configuration for monotonic reads
    hash_virtual_nodes: int = 40

    # Read behavior
    read_after_write_consistency: bool = True  # Read from primary after write
    transaction_read_primary: bool = True  # Read from primary in transactions

    # Random read selection
    random_seed: int | None = None

    def __post_init__(self):
        """Initialize hash ring if needed."""
        if self.strategy == "consistent_hash":
            self._hash_ring = HashRing(
                nodes=self.replica_aliases,
                virtual_nodes=self.hash_virtual_nodes,
            )
        else:
            self._hash_ring = None

        if self.random_seed is not None:
            random.seed(self.random_seed)

    @property
    def all_aliases(self) -> list[str]:
        """Get all database aliases including primary and replicas."""
        return [self.primary_alias] + self.replica_aliases

    def get_replica_for_user(self, user_id: str) -> str:
        """
        Get the replica alias for a specific user using consistent hashing.

        Args:
            user_id: The user identifier

        Returns:
            The replica alias for this user
        """
        if not self.replica_aliases:
            return self.primary_alias
        if self._hash_ring is None:
            self._hash_ring = HashRing(
                nodes=self.replica_aliases,
                virtual_nodes=self.hash_virtual_nodes,
            )
        result = self._hash_ring.get_node(str(user_id))
        return result if result is not None else self.primary_alias

    def get_random_replica(self) -> str:
        """Get a random replica alias."""
        if not self.replica_aliases:
            return self.primary_alias
        return random.choice(self.replica_aliases)


# Global configuration instance
_config: MongoRouterConfig | None = None


def get_config() -> MongoRouterConfig:
    """Get the global configuration instance."""
    global _config
    if _config is None:
        _config = MongoRouterConfig()
    return _config


def set_config(config: MongoRouterConfig) -> None:
    """Set the global configuration instance."""
    global _config
    _config = config


def configure(
    primary_alias: str = "default",
    replica_aliases: list[str] | None = None,
    strategy: Literal["random", "transaction_aware", "consistent_hash"] = "transaction_aware",
    hash_virtual_nodes: int = 40,
    read_after_write_consistency: bool = True,
    transaction_read_primary: bool = True,
    random_seed: int | None = None,
) -> MongoRouterConfig:
    """
    Configure the mongoengine read-write router.

    Args:
        primary_alias: The database alias for the primary (write) database
        replica_aliases: List of database aliases for read replicas
        strategy: The routing strategy to use
        hash_virtual_nodes: Number of virtual nodes for consistent hashing
        read_after_write_consistency: Whether to read from primary after writes
        transaction_read_primary: Whether to read from primary in transactions
        random_seed: Optional seed for random replica selection

    Returns:
        The created configuration instance
    """
    if replica_aliases is None:
        replica_aliases = ["replica1", "replica2"]

    config = MongoRouterConfig(
        primary_alias=primary_alias,
        replica_aliases=replica_aliases,
        strategy=strategy,
        hash_virtual_nodes=hash_virtual_nodes,
        read_after_write_consistency=read_after_write_consistency,
        transaction_read_primary=transaction_read_primary,
        random_seed=random_seed,
    )
    set_config(config)
    return config
