"""
Normalize keyboard/mouse/average activity percentages for DB storage and API payloads.

Values greater than zero are ceiled to whole percents (0.4 -> 1, 12.2 -> 13). Zero stays zero.
"""
from __future__ import annotations

import math
from typing import Any, Dict, Optional, Union

ActivityLevelDict = Dict[str, str]

_DEFAULT_LEVEL: ActivityLevelDict = {
    "mouse": "0.0",
    "average": "0.0",
    "keyboard": "0.0",
}


def ceil_activity_percent(value: Any) -> str:
    """Ceil a single activity percentage; non-positive values become ``0.0``."""
    try:
        v = float(value if value is not None else 0)
    except (TypeError, ValueError):
        return "0.0"
    if v <= 0:
        return "0.0"
    return str(int(math.ceil(v)))


def normalize_activity_level(
    level: Optional[Union[Dict[str, Any], ActivityLevelDict]],
    minimum_percent: float = 0,
) -> ActivityLevelDict:
    """
    Normalize an activity level dict for persistence or API upload.

    Args:
        level: Raw activity level with mouse/keyboard/average keys.
        minimum_percent: If > 0, any metric that is > 0 after ceil but below this
            floor is raised to this value (used for idle-popup resolution).
    """
    if not level or not isinstance(level, dict):
        level = {}

    result: ActivityLevelDict = {
        "mouse": ceil_activity_percent(level.get("mouse")),
        "keyboard": ceil_activity_percent(level.get("keyboard")),
        "average": ceil_activity_percent(level.get("average")),
    }

    if minimum_percent <= 0:
        return result

    min_str = str(int(minimum_percent))
    for key in ("mouse", "keyboard", "average"):
        try:
            v = float(result[key])
        except (TypeError, ValueError):
            v = 0.0
        if v > 0 and v < minimum_percent:
            result[key] = min_str

    return result


def normalize_activity_level_for_idle_resolution(
    level: Optional[Union[Dict[str, Any], ActivityLevelDict]],
    minimum_percent: float = 1,
) -> ActivityLevelDict:
    """
    Ceil activity fields and ensure at least ``minimum_percent`` when resolving idle popup.

    Fully idle intervals (all zeros after ceil) are raised to the minimum on all metrics.
    """
    normalized = normalize_activity_level(level)
    try:
        avg = float(normalized.get("average", 0) or 0)
    except (TypeError, ValueError):
        avg = 0.0
    if avg <= 0:
        min_str = str(int(minimum_percent))
        return {"mouse": min_str, "keyboard": min_str, "average": min_str}
    return normalize_activity_level(level, minimum_percent=minimum_percent)
