"""FALL-R2-CONFIG-02: bounded, local angle overrides; timing remains fixed."""
from dataclasses import asdict, dataclass
import math

ANGLE_LIMITS = {
    'r1_torso_min_deg': (50., 60.),
    'r1_leg_min_deg': (50., 60.),
    'd1_knee_min_deg': (140., 160.),
}

class R2ConfigError(ValueError):
    pass


def angle_value(value, minimum, maximum):
    try:
        valid = type(value) in (int, float) and math.isfinite(value) and minimum <= value <= maximum
    except OverflowError:
        valid = False
    if not valid:
        raise R2ConfigError('FALL_R2_ANGLE_INVALID')
    return float(value)


@dataclass(frozen=True)
class R2Config:
    schema_version: int = 1
    r1_torso_min_deg: float = 50.
    r1_leg_min_deg: float = 50.
    d1_knee_min_deg: float = 140.

    def __post_init__(self):
        if type(self.schema_version) is not int or self.schema_version != 1:
            raise R2ConfigError('FALL_R2_SCHEMA_VERSION_UNSUPPORTED')
        for key, bounds in ANGLE_LIMITS.items():
            angle_value(getattr(self, key), *bounds)

    def payload(self):
        value = asdict(self)
        value.update({key: float(value[key]) for key in ANGLE_LIMITS})
        return value

    @classmethod
    def parse(cls, value):
        block(value)
        return cls(**value)

    def patched(self, value):
        # The existing Web reset owns common scores/cooldown, never these angles.
        if block(value, request=True):
            return self
        return self.parse(dict(self.payload(), **value))


def score_floor(value, minimum):
    try:valid=type(value) in (int,float) and math.isfinite(value) and 0<=value<=1
    except OverflowError:valid=False
    if not valid:raise R2ConfigError('FALL_R2_SCORE_INVALID')
    return max(float(value),minimum)

def block(value, *, request=False):
    allowed={'schema_version', *ANGLE_LIMITS}
    if request:allowed.add('reset_to_defaults')
    if type(value) is not dict or set(value)-allowed:raise R2ConfigError('FALL_R2_FIELDS_INVALID')
    if 'schema_version' not in value:raise R2ConfigError('FALL_R2_SCHEMA_VERSION_REQUIRED')
    if type(value['schema_version']) is not int or value['schema_version']!=1:raise R2ConfigError('FALL_R2_SCHEMA_VERSION_UNSUPPORTED')
    if 'reset_to_defaults' in value:
        if value['reset_to_defaults'] is not True or set(value)&set(ANGLE_LIMITS):
            raise R2ConfigError('FALL_R2_RESET_INVALID')
    for key, bounds in ANGLE_LIMITS.items():
        if key in value:angle_value(value[key], *bounds)
    return value.get('reset_to_defaults',False)
