"""Bounded interaction timing; never generates misses or extra inputs."""
from __future__ import annotations

import math
import random

from .errors import MiniError, require


DEFAULT_REPEAT_INTERVAL = (1.0, 3.0)
# Independent of the user cycle interval (which may legitimately be zero).
# Newly revealed result buttons are visible before their click handlers settle.
RESULT_BUTTON_SETTLE = (2.5, 3.5)
RESULT_REVEAL_SETTLE = 3.0
RESULT_REVEAL_RETRY = 8.0
ABYSS_RETRY_RECHECK = 15.0
DUNGEON_UNKNOWN_LIMIT = 30 * 60
ABYSS_FOOD_INTERVAL = 2 * 60
PROCESSING_INPUT_SPEED = 1.5
INPUT_DELAYS = {
    "before": (0.12, 0.32, 0.20),
    "hold": (0.055, 0.13, 0.085),
    "after": (0.12, 0.30, 0.18),
    "wheel": (0.10, 0.18, 0.13),
}


def input_delay(phase, rng=None):
    return (rng or random.SystemRandom()).triangular(*INPUT_DELAYS[phase])


def coordinate_interval_seconds(store=None):
    """Saved user pacing, shared by every coordinate tape and child service."""
    from .execution_rules import number
    default = number("input.coordinate_interval_seconds", .8)
    if type(default) not in (int, float) or not .1 <= default <= 60 or not math.isfinite(default):
        default = .8
    preference = getattr(store, "preference", None)
    value = preference(("input", "coordinate_interval_seconds"), default) if callable(preference) else default
    if type(value) not in (int, float) or not .1 <= value <= 60 or not math.isfinite(value):
        return float(default)
    return float(value)


def coordinate_interval_randomized(store=None):
    """Default ON for new and existing settings that have no explicit choice."""
    preference = getattr(store, "preference", None)
    value = preference(("input", "coordinate_interval_randomized"), True) if callable(preference) else True
    return value if type(value) is bool else True


def sample_input_interval(base, randomized=True, rng=None):
    """Sample once per input gap; never use this function as a poll condition."""
    return (rng or random.SystemRandom()).uniform(base, base + .25) if randomized else float(base)


def fishing_movement_hold_seconds(rng=None):
    """One draw for both members of an S/W movement pair."""
    return (rng or random.SystemRandom()).uniform(.131, .159)


def repeat_interval(minimum, maximum):
    try:
        low, high = float(minimum), float(maximum)
    except (TypeError, ValueError):
        raise MiniError("재실행 간격은 초 단위 숫자로 입력해 주세요.") from None
    require(math.isfinite(low) and math.isfinite(high) and 0 <= low <= high,
            "재실행 간격은 0 이상이며 최소 ≤ 최대여야 합니다.")
    return low, high


def repeat_delay(interval, rng=None):
    low, high = repeat_interval(*interval)
    return (rng or random.SystemRandom()).uniform(low, high)
