"""
Transaction type constants for wallet transactions.

These constants represent different types of wallet transactions as integers.
Use these constants when calling add_point() or deduct_point() methods.

Custom Transaction Types:
    Developers can extend this module by registering custom transaction types
    using the register_custom_transaction_type() function. Custom types should
    use the reserved range 10000-19999 to avoid conflicts with built-in types.
"""

# Reserved range for custom transaction types (10000-19999)
# Developers should use this range when registering custom transaction types
CUSTOM_TRANSACTION_TYPE_RANGE_START = 10000
CUSTOM_TRANSACTION_TYPE_RANGE_END = 19999

# Wallet Operations (1000-1999)
WALLET_DEPOSIT = 1000
WALLET_DEPOSIT_CANCEL = 1001
WALLET_TRANSFER = 1002
WALLET_WITHDRAW = 1003
WALLET_WITHDRAW_REFUND = 1004
WALLET_ADJUST = 1005
WALLET_TOPUP = 1006
WALLET_DEDUCT = 1007

# Package Operations (2000-2999)
PACKAGE_ACTIVATION = 2000
PACKAGE_ACTIVATION_CANCEL = 2001
PACKAGE_UPGRADE = 2002
PACKAGE_UPGRADE_CANCEL = 2003
PACKAGE_RENEWAL = 2004
PACKAGE_RENEWAL_CANCEL = 2005
PACKAGE_EXPIRY_REFUND = 2006

# Commission & Rewards (3000-3999)
COMMISSION_DISTRIBUTION = 3000
COMMISSION_DISTRIBUTION_REVERSE = 3001
ROI_DISTRIBUTION = 3100
ROI_DISTRIBUTION_REVERSE = 3101

# Product Operations (4000-4999)
PRODUCT_ORDER = 4000
PRODUCT_ORDER_CANCEL = 4001
PRODUCT_ORDER_REFUND = 4002
PRODUCT_ORDER_PARTIAL_REFUND = 4003
PRODUCT_REDEMPTION = 4004
PRODUCT_REDEMPTION_CANCEL = 4005

# System Operations (5000-5999)
SYSTEM_ADJUSTMENT = 5000
SYSTEM_REWARD = 5001
SYSTEM_PENALTY = 5002
SYSTEM_REVERSAL = 5003
SYSTEM_MIGRATION = 5004

# Built-in transaction types dictionary
_BUILTIN_TRANSACTION_TYPES = {
    # Wallet Operations
    "wallet-deposit": WALLET_DEPOSIT,
    "wallet-deposit-cancel": WALLET_DEPOSIT_CANCEL,
    "wallet-transfer": WALLET_TRANSFER,
    "wallet-withdraw": WALLET_WITHDRAW,
    "wallet-withdraw-refund": WALLET_WITHDRAW_REFUND,
    "wallet-adjust": WALLET_ADJUST,
    "wallet-topup": WALLET_TOPUP,
    "wallet-deduct": WALLET_DEDUCT,
    # Package Operations
    "package-activation": PACKAGE_ACTIVATION,
    "package-activation-cancel": PACKAGE_ACTIVATION_CANCEL,
    "package-upgrade": PACKAGE_UPGRADE,
    "package-upgrade-cancel": PACKAGE_UPGRADE_CANCEL,
    "package-renewal": PACKAGE_RENEWAL,
    "package-renewal-cancel": PACKAGE_RENEWAL_CANCEL,
    "package-expiry-refund": PACKAGE_EXPIRY_REFUND,
    # Commission & Rewards
    "commission-distribution": COMMISSION_DISTRIBUTION,
    "commission-distribution-reverse": COMMISSION_DISTRIBUTION_REVERSE,
    "roi-distribution": ROI_DISTRIBUTION,
    "roi-distribution-reverse": ROI_DISTRIBUTION_REVERSE,
    # Product Operations
    "product-order": PRODUCT_ORDER,
    "product-order-cancel": PRODUCT_ORDER_CANCEL,
    "product-order-refund": PRODUCT_ORDER_REFUND,
    "product-order-partial-refund": PRODUCT_ORDER_PARTIAL_REFUND,
    "product-redemption": PRODUCT_REDEMPTION,
    "product-redemption-cancel": PRODUCT_REDEMPTION_CANCEL,
    # System Operations
    "system-adjustment": SYSTEM_ADJUSTMENT,
    "system-reward": SYSTEM_REWARD,
    "system-penalty": SYSTEM_PENALTY,
    "system-reversal": SYSTEM_REVERSAL,
    "system-migration": SYSTEM_MIGRATION,
}

# Custom transaction types dictionary (for developer extensions)
_CUSTOM_TRANSACTION_TYPES: dict[str, int] = {}

# Combined dictionary mapping for easy lookup (built-in + custom)
TRANSACTION_TYPES = {**_BUILTIN_TRANSACTION_TYPES, **_CUSTOM_TRANSACTION_TYPES}

# Reverse mapping for getting string name from integer
TRANSACTION_TYPE_NAMES = {v: k for k, v in TRANSACTION_TYPES.items()}


def get_transaction_type(name: str) -> int:
    """
    Get transaction type integer constant from string name.
    
    Args:
        name: Transaction type name (e.g., "wallet-deposit")
        
    Returns:
        Transaction type integer constant
        
    Raises:
        ValueError: If transaction type name is not found
    """
    if name not in TRANSACTION_TYPES:
        raise ValueError(f"Unknown transaction type: {name}")
    return TRANSACTION_TYPES[name]


def get_transaction_type_name(trans_type: int) -> str | None:
    """
    Get transaction type string name from integer constant.
    
    Args:
        trans_type: Transaction type integer constant
        
    Returns:
        Transaction type name string, or None if not found
    """
    return TRANSACTION_TYPE_NAMES.get(trans_type)


def register_custom_transaction_type(name: str, value: int, override: bool = False) -> None:
    """
    Register a custom transaction type.
    
    This function allows developers to extend the transaction type system with
    their own custom types. Custom types should use the reserved range
    10000-19999 to avoid conflicts with built-in types.
    
    Args:
        name: Transaction type name (e.g., "custom-reward")
        value: Transaction type integer constant (should be in range 10000-19999)
        override: If True, allow overriding existing custom types (default: False)
        
    Raises:
        ValueError: If value is not in the custom range (10000-19999)
        ValueError: If name already exists and override=False
        ValueError: If value already exists and override=False
        
    Example:
        # Register a custom transaction type
        register_custom_transaction_type("custom-reward", 10000)
        
        # Use it in transactions
        from django_wallet_utils.transaction_types import get_transaction_type
        custom_type = get_transaction_type("custom-reward")  # Returns 10000
        
        service.add_point(
            user_id=123,
            point_type="credit_balance",
            amount=Decimal("100.00"),
            remarks="Custom reward",
            trans_type=custom_type,
        )
    """
    global TRANSACTION_TYPES, TRANSACTION_TYPE_NAMES, _CUSTOM_TRANSACTION_TYPES
    
    # Validate value is in custom range
    if not (CUSTOM_TRANSACTION_TYPE_RANGE_START <= value <= CUSTOM_TRANSACTION_TYPE_RANGE_END):
        raise ValueError(
            f"Custom transaction type value {value} must be in range "
            f"{CUSTOM_TRANSACTION_TYPE_RANGE_START}-{CUSTOM_TRANSACTION_TYPE_RANGE_END}"
        )
    
    # Check if name already exists
    if name in TRANSACTION_TYPES:
        if not override:
            existing_value = TRANSACTION_TYPES[name]
            raise ValueError(
                f"Transaction type name '{name}' already exists with value {existing_value}. "
                "Use override=True to replace it."
            )
    
    # Check if value already exists (unless overriding the same name)
    existing_name = TRANSACTION_TYPE_NAMES.get(value)
    if existing_name and existing_name != name:
        if not override:
            raise ValueError(
                f"Transaction type value {value} already exists with name '{existing_name}'. "
                "Use override=True to replace it."
            )
    
    # Register the custom type
    _CUSTOM_TRANSACTION_TYPES[name] = value
    
    # Update combined dictionaries
    TRANSACTION_TYPES = {**_BUILTIN_TRANSACTION_TYPES, **_CUSTOM_TRANSACTION_TYPES}
    TRANSACTION_TYPE_NAMES = {v: k for k, v in TRANSACTION_TYPES.items()}


def unregister_custom_transaction_type(name: str) -> None:
    """
    Unregister a custom transaction type.
    
    Args:
        name: Transaction type name to unregister
        
    Raises:
        ValueError: If name is a built-in type (cannot unregister built-in types)
        ValueError: If name is not registered
        
    Example:
        register_custom_transaction_type("custom-reward", 10000)
        # ... use it ...
        unregister_custom_transaction_type("custom-reward")
    """
    global TRANSACTION_TYPES, TRANSACTION_TYPE_NAMES, _CUSTOM_TRANSACTION_TYPES
    
    # Check if it's a built-in type
    if name in _BUILTIN_TRANSACTION_TYPES:
        raise ValueError(f"Cannot unregister built-in transaction type '{name}'")
    
    # Check if it exists in custom types
    if name not in _CUSTOM_TRANSACTION_TYPES:
        raise ValueError(f"Custom transaction type '{name}' is not registered")
    
    # Remove from custom types
    del _CUSTOM_TRANSACTION_TYPES[name]
    
    # Update combined dictionaries
    TRANSACTION_TYPES = {**_BUILTIN_TRANSACTION_TYPES, **_CUSTOM_TRANSACTION_TYPES}
    TRANSACTION_TYPE_NAMES = {v: k for k, v in TRANSACTION_TYPES.items()}


def get_custom_transaction_types() -> dict[str, int]:
    """
    Get all registered custom transaction types.
    
    Returns:
        Dictionary mapping custom transaction type names to their integer values
        
    Example:
        custom_types = get_custom_transaction_types()
        print(custom_types)  # {'custom-reward': 10000, 'custom-penalty': 10001}
    """
    return _CUSTOM_TRANSACTION_TYPES.copy()
