"""IGUANA — pure index-trend functions (port of iguana-producer.py v1.0.1).

Ported VERBATIM from senpi-skills/iguana/scripts/iguana-producer.py (the pure
trend / scoring helpers, already unit-tested in that repo's tests/test_signal.py)
and iguana_config.py. No I/O, no MCP, no daemon — pure and unit-testable.
`scan.py` fetches 4h candles via ctx.senpi_mcp and hands them to these helpers.

THE STRATEGY — XYZ macro index trend (the simplest XYZ equity exposure):
trend-follow the broad indices `xyz:SP500` + `xyz:XYZ100`. No stock-picking, no
commodities, no pre-IPO. Two assets, ONE decision per tick. Each tick computes a
4-day trend strength (% change of the latest 4h close vs the close
`trendLookbackBars` (24 = 4 days) ago), picks the stronger move past
`minTrendPct` (1.5%), and trades in its direction. Score = base 3, +2 if the move
is strong (>= `strongTrendPct` 4.0%), +1 if volume is rising (> 15% over 6 bars);
floor `minScore` 4. Closest thing to an index fund, but 24/7 on Hyperliquid.

Constants below are the source defaults (iguana-producer.py + config v1.0.0).
"""

# ── Constants (verbatim from producer + config v1.0.0) ──

MAX_LEVERAGE = 5
DEFAULT_LEVERAGE = 3
DEFAULT_MIN_SCORE = 4

DEFAULT_WHITELIST = ["xyz:SP500", "xyz:XYZ100"]
DEFAULT_TREND_LOOKBACK = 24    # 24 × 4h bars = 4 days
DEFAULT_MIN_TREND_PCT = 1.5    # minimum |4-day move| to call it a trend
DEFAULT_STRONG_TREND_PCT = 4.0
DEFAULT_MARGIN_PCT = 0.20

# Score normalization divisor used by the source when posting the [0,1] wire
# score (min(score / 6, 1.0)). Kept for parity — the scaffold/runtime own wire
# scoring now; the raw additive score rides on data{}.
SCORE_NORMALIZATION_DIVISOR = 6.0


def _f(c, primary, alt=None, default=0.0):
    """Pull a numeric field from a candle dict, trying `primary` then `alt`.

    Verbatim from iguana-producer._f — candle responses use either long keys
    (close/volume) or short keys (c/v) depending on the source shape.
    """
    val = c.get(primary)
    if val is None and alt:
        val = c.get(alt)
    try:
        return float(val if val is not None else default)
    except (TypeError, ValueError):
        return default


# ═══════════════════════════════════════════════════════════════
# Pure index-trend logic (unit-tested in the source tests/test_signal.py)
# ═══════════════════════════════════════════════════════════════

def trend_strength(closes, lookback):
    """% change of the latest close vs the close `lookback` bars ago.
    None if insufficient data or the reference price is non-positive.

    Verbatim from iguana-producer.trend_strength.
    """
    if not closes or len(closes) <= lookback:
        return None
    ref = closes[-(lookback + 1)]
    latest = closes[-1]
    if ref is None or ref <= 0:
        return None
    return ((latest - ref) / ref) * 100.0


def trend_direction(strength, min_pct):
    """Direction implied by trend strength. None if magnitude below threshold.

    Verbatim from iguana-producer.trend_direction.
    """
    if strength is None or abs(strength) < min_pct:
        return None
    return "LONG" if strength > 0 else "SHORT"


def pick_strongest_trend(per_asset_strength, min_pct):
    """Among {asset: strength}, return the asset with the highest |strength|
    above min_pct. Returns (asset, strength) or None.

    Verbatim from iguana-producer.pick_strongest_trend.
    """
    best, best_mag = None, -1.0
    for asset, strength in per_asset_strength.items():
        if strength is None:
            continue
        mag = abs(strength)
        if mag < min_pct:
            continue
        if mag > best_mag:
            best_mag, best = mag, (asset, strength)
    return best


def volume_trend(candles, lookback=6):
    """% change of the recent half vs the earlier half of the last `lookback`
    candle volumes. 0.0 when there is insufficient data.

    Verbatim from iguana-producer.volume_trend.
    """
    if len(candles) < lookback:
        return 0.0
    vols = [_f(c, "volume", "v") for c in candles[-lookback:]]
    half = lookback // 2
    if half <= 0:
        return 0.0
    recent = sum(vols[-half:]) / half
    earlier = sum(vols[:half]) / half
    if earlier <= 0:
        return 0.0
    return ((recent - earlier) / earlier) * 100


def build_thesis(asset, strength, candles, config):
    """Build the index-trend thesis (direction + additive score + reasons).

    Score: base 3 (trend above min), +2 if |strength| >= strongTrendPct, +1 if
    volume_trend > 15%. Returns the thesis dict or None when the direction gate
    (|strength| >= minTrendPct) is not cleared. Verbatim from
    iguana-producer.build_thesis.
    """
    min_pct = float(config.get("minTrendPct", DEFAULT_MIN_TREND_PCT))
    strong_pct = float(config.get("strongTrendPct", DEFAULT_STRONG_TREND_PCT))

    direction = trend_direction(strength, min_pct)
    if direction is None:
        return None

    vol = volume_trend(candles)

    score = 3   # base — trend strength above min
    reasons = [f"{asset}_4d_trend_{strength:+.1f}%"]
    if abs(strength) >= strong_pct:
        score += 2
        reasons.append(f"trend_strong_{strength:+.1f}%")
    if vol > 15:
        score += 1
        reasons.append(f"vol_rising_{vol:+.0f}%")

    return {
        "coin": asset,
        "direction": direction,
        "score": score,
        "reasons": reasons,
        "trend_pct": round(strength, 2),
        "volume_trend_pct": round(vol, 2),
    }
