"""Resampling helpers: upsample / downsample bar series."""

from __future__ import annotations

from typing import List

try:
    import pandas as pd
    _HAS_PANDAS = True
except ImportError:
    _HAS_PANDAS = False

from sd_api.models.kline import KlineBar


def resample_bars(
    bars: List[KlineBar],
    target_tf: str,
) -> List[KlineBar]:
    """Resample a list of KlineBars to a new timeframe.

    Parameters
    ----------
    bars : List[KlineBar]
        Source bars in chronological order.
    target_tf : str
        Target timeframe in seconds (e.g. 300 for 5min, 3600 for 1h).

    Returns
    -------
    List[KlineBar]
        Resampled bars.
    """
    if not _HAS_PANDAS:
        raise ImportError("pandas is required for resample_bars")
    if not bars:
        return []

    # Build a DataFrame
    records = [b.to_dict() for b in bars]
    df = pd.DataFrame(records)
    df["ts_dt"] = pd.to_datetime(df["ts"], unit="ms", utc=True)
    df.set_index("ts_dt", inplace=True)

    # Parse target_tf to Pandas offset alias
    tf_seconds = _tf_to_seconds(target_tf)
    rule = _seconds_to_pandas_rule(tf_seconds)

    resampled = df.resample(rule).agg({
        "open": "first",
        "high": "max",
        "low": "min",
        "close": "last",
        "ts": "first",
    }).dropna(subset=["high"])

    result: List[KlineBar] = []
    for _, row in resampled.iterrows():
        result.append(KlineBar(
            ts=int(row["ts"]),
            open=float(row["open"]),
            high=float(row["high"]),
            low=float(row["low"]),
            close=float(row["close"]),
        ))
    return result


def _tf_to_seconds(tf: str) -> int:
    mapping = {
        "1min": 60, "5min": 300, "10min": 600,
        "15min": 900, "30min": 1800,
        "1hour": 3600, "4hour": 14400, "1day": 86400,
    }
    if tf in mapping:
        return mapping[tf]
    # Try parsing "5m", "1h", etc.
    import re
    m = re.match(r"(\d+)([mhd])", tf.lower())
    if m:
        val, unit = int(m.group(1)), m.group(2)
        multipliers = {"m": 60, "h": 3600, "d": 86400}
        return val * multipliers.get(unit, 60)
    return 60  # fallback to 1min


def _seconds_to_pandas_rule(seconds: int) -> str:
    if seconds < 3600:
        return f"{seconds}s"
    hours = seconds // 3600
    if seconds % 3600 == 0:
        return f"{hours}h"
    return f"{seconds}s"
