import datetime
import logging
import os
import random
import re
import string
from contextlib import contextmanager
from math import floor, log10
from pathlib import Path

import numpy as np

log = logging.getLogger(__name__)


def significant_digits(value: float, sig_digits: int = 4) -> float:
    """Round a number to a chosen number of significant digits."""
    if value == 0:
        return 0
    order = floor(log10(abs(value)))
    digits_after_point = sig_digits - order - 1
    return round(value, digits_after_point)


def format_number(value, decimal_sep=",", thousand_sep=" "):
    """Format a number with thousand/decimal separators, removing trailing zeros."""
    if value is None:
        return ""
    if not isinstance(value, (int, float)):
        return value
    return (
        f"{value:,f}".rstrip("0").rstrip(".").replace(",", "X").replace(".", decimal_sep).replace("X", thousand_sep)
    )


def set_random_seed(input_string: str) -> None:
    """Seed both random and numpy from a string so sampling is reproducible between runs."""
    random.seed(input_string)
    np.random.seed(random.randrange(1000))


def random_id(n: int = 4, time: bool = True) -> str:
    """Return a random string of n chars, optionally prefixed with YYYYMMDD_HHMM.
    Uses a fresh Random() instance to avoid being affected by set_random_seed()."""
    generator = random.Random()
    generator.seed()
    result = "".join(generator.choices(string.ascii_lowercase + string.digits, k=n))
    if time:
        result = datetime.datetime.now().strftime("%Y%m%d_%H%M_") + result
    return result


def remove_random_id(text: str) -> str:
    """Strip a leading random_id prefix from a filename."""
    return re.sub(r"^(20\d{6}_\d{4}_)?[a-z0-9]{4}_", "", text)


@contextmanager
def chdir(path: Path):
    """Context manager that temporarily changes the working directory."""
    oldpwd = os.getcwd()
    os.chdir(path)
    try:
        yield
    finally:
        os.chdir(oldpwd)
