"""Randomize numbers by applying a random percentage variation."""

import contextlib
import logging
import re
import secrets
import sys

_logger = logging.getLogger(__name__)

# Use cryptographically secure random number generator
_rng = secrets.SystemRandom()

DEFAULT_MIN_PERCENTAGE = 1
DEFAULT_MAX_PERCENTAGE = 20


def randomize_numbers(
    nums: list[float],
    min_pct: float = DEFAULT_MIN_PERCENTAGE,
    max_pct: float = DEFAULT_MAX_PERCENTAGE,
) -> list[float]:
    """Apply random percentage variation to a list of numbers."""
    result = []
    for number in nums:
        percentage = _rng.uniform(min_pct, max_pct) / 100
        if _rng.choice([True, False]):
            new_number = number + (number * percentage)
        else:
            new_number = number - (number * percentage)
        result.append(new_number)
    return result


def parse_input(text: str) -> tuple[list[float], list[int]]:
    """Parse a string of numbers and return floats with decimal counts."""
    # Replace commas with dots and remove non-numeric characters
    # except dots, commas, and digits
    cleaned_input = re.sub(r"[^\d.,\s]", "", text).replace(",", ".")
    # Split the cleaned input into individual numbers
    number_strings = cleaned_input.split()
    # Convert the number strings to floats, filtering out invalid ones
    parsed_results = [
        result
        for num_str in number_strings
        if (result := _parse_single_number(num_str)) is not None
    ]
    nums = [float_num for float_num, _ in parsed_results]
    decimals = [digits_count for _, digits_count in parsed_results]
    return nums, decimals


def _parse_single_number(num_str: str) -> tuple[float, int] | None:
    """Parse a single number string into float and decimal count.

    Args:
        num_str: The number string to parse.

    Returns:
        Tuple of (float value, decimal count) or None if invalid.
    """
    try:
        float_num = float(num_str)
        digits_count = len(num_str.rsplit(".", maxsplit=1)[-1]) if "." in num_str else 0
    except ValueError:
        return None
    return float_num, digits_count


MIN_ARGS = 2


def main() -> None:
    """Run the number randomizer from command line arguments."""
    if len(sys.argv) < MIN_ARGS:
        _logger.info(
            "Usage: python random_digits.py <number1> <number2> ... "
            "[min_percentage max_percentage]"
        )
        sys.exit(1)

    args_string = " ".join(sys.argv[1:])
    numbers, decimal_counts = parse_input(args_string)

    if not numbers:
        _logger.error("No valid numbers provided.")
        sys.exit(1)

    min_pct = DEFAULT_MIN_PERCENTAGE
    max_pct = DEFAULT_MAX_PERCENTAGE

    try:
        if len(sys.argv) > len(numbers) + 1:
            with contextlib.suppress(ValueError):
                min_pct = float(sys.argv[len(numbers) + 1])
        if len(sys.argv) > len(numbers) + 2:
            with contextlib.suppress(ValueError):
                max_pct = float(sys.argv[len(numbers) + 2])

        randomized = randomize_numbers(numbers, min_pct, max_pct)
        formatted_numbers = []
        for i, num in enumerate(randomized):
            format_str = f".{decimal_counts[i]}f"
            formatted_numbers.append(float(format(num, format_str)))

        _logger.info("Original numbers: %s", numbers)
        _logger.info("Randomized numbers: %s", formatted_numbers)
    except ValueError:
        _logger.exception("Error processing numbers")
        _logger.exception("Please provide valid numbers and percentages.")
        sys.exit(1)


if __name__ == "__main__":
    main()
