from __future__ import annotations

import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

import polars as pl

HORIZON_SECONDS = {"30s": 30, "1m": 60, "2m": 120, "5m": 300}


def ensure_utc_timestamp(frame: pl.DataFrame, column: str) -> pl.DataFrame:
    return frame.with_columns(
        pl.col(column)
        .cast(pl.Datetime(time_zone="UTC"), strict=False)
        .dt.replace_time_zone("UTC")
        .alias(column)
    )


def parse_feature_payload(value: str | None) -> dict[str, float]:
    if not value:
        return {}
    try:
        parsed = json.loads(value)
    except json.JSONDecodeError:
        return {}
    if not isinstance(parsed, dict):
        return {}
    result: dict[str, float] = {}
    for key, item in parsed.items():
        try:
            result[str(key)] = float(item)
        except (TypeError, ValueError):
            continue
    return result


def encode_feature_payload(values: dict[str, Any]) -> str:
    compact = {
        key: float(value)
        for key, value in values.items()
        if value is not None and not isinstance(value, bool)
    }
    return json.dumps(compact, separators=(",", ":"), sort_keys=True)


def horizon_to_seconds(horizon: str) -> int:
    if horizon not in HORIZON_SECONDS:
        raise ValueError(f"Unsupported horizon: {horizon}")
    return HORIZON_SECONDS[horizon]


def safe_div(numerator: float | None, denominator: float | None) -> float | None:
    if numerator is None or denominator in (None, 0):
        return None
    return float(numerator) / float(denominator if denominator is not None else 1.0)


def utc_now() -> datetime:
    return datetime.now(tz=timezone.utc)


def candidate_data_roots(root: Path) -> list[Path]:
    return [root]
