"""Dependency-free inline-SVG charts. Server-rendered so the cockpit needs no JS charting library — the SVG
string is embedded directly in the HTML. nav_sparkline maps a NAV series to a polyline scaled to the box."""
from __future__ import annotations


def nav_sparkline(nav: list[float], *, width: int = 120, height: int = 28,
                  color: str = "#3fb950") -> str:
    box = f'<svg width="{width}" height="{height}" viewBox="0 0 {width} {height}" '\
          'xmlns="http://www.w3.org/2000/svg">'
    if not nav:
        return box + "</svg>"
    lo, hi = min(nav), max(nav)
    span = (hi - lo) or 1.0
    n = len(nav)
    step = width / (n - 1) if n > 1 else 0.0
    pts = []
    for i, v in enumerate(nav):
        x = i * step
        y = height - (v - lo) / span * (height - 2) - 1     # 1px padding, y inverted
        pts.append(f"{x:.1f},{y:.1f}")
    stroke = color if nav[-1] >= nav[0] else "#f85149"
    return (box + f'<polyline fill="none" stroke="{stroke}" stroke-width="1.5" '
            f'points="{" ".join(pts)}" /></svg>')


def equity_curve(equity: list[float], *, width: int = 600, height: int = 180,
                 color: str = "#3fb950") -> str:
    """The sim's equity curve with a movable cursor marker for the timeline slider.

    Same polyline scaling as `nav_sparkline`, but the geometry (lo / span / left-x step) is published
    on the `<svg>` as data-* attributes so the inline slider script can place the cursor — a vertical
    line + a dot — at the exact (x, y) of any sample WITHOUT re-fetching. The cursor elements start at
    the last sample and are moved client-side; the SVG remains a self-contained string (no JS needed to
    render the static curve)."""
    pad = 2.0
    box = (f'<svg id="sim-curve" width="{width}" height="{height}" viewBox="0 0 {width} {height}" '
           f'data-lo="{0.0}" data-span="{1.0}" data-step="{0.0}" data-pad="{pad}" '
           'xmlns="http://www.w3.org/2000/svg" style="border:1px solid #21262d;border-radius:8px">')
    if not equity:
        return box + "</svg>"
    lo, hi = min(equity), max(equity)
    span = (hi - lo) or 1.0
    n = len(equity)
    step = width / (n - 1) if n > 1 else 0.0

    def _y(v: float) -> float:
        return height - (v - lo) / span * (height - 2 * pad) - pad

    pts = [f"{i * step:.1f},{_y(v):.1f}" for i, v in enumerate(equity)]
    stroke = color if equity[-1] >= equity[0] else "#f85149"
    last_x = (n - 1) * step
    last_y = _y(equity[-1])
    # Republish the real geometry so the client can compute cursor positions identically.
    box = box.replace(f'data-lo="{0.0}" data-span="{1.0}" data-step="{0.0}"',
                      f'data-lo="{lo:.6f}" data-span="{span:.6f}" data-step="{step:.6f}"')
    return (box
            + f'<polyline fill="none" stroke="{stroke}" stroke-width="1.5" points="{" ".join(pts)}" />'
            + f'<line id="sim-cursor-line" x1="{last_x:.1f}" y1="0" x2="{last_x:.1f}" y2="{height}" '
              'stroke="#8b949e" stroke-width="1" stroke-dasharray="3,3" />'
            + f'<circle id="sim-cursor-dot" cx="{last_x:.1f}" cy="{last_y:.1f}" r="3.5" '
              'fill="#58a6ff" />'
            + "</svg>")


def overlay_curve(real: list[float], plan: list[float], *, width: int = 600, height: int = 180,
                  real_color: str = "#3fb950", plan_color: str = "#8b949e") -> str:
    """The holdings detail page's "value over time" curve (D2.2/2.3): the REAL account value (solid) with
    the MODELED plan overlaid (DASHED) on one shared, jointly-scaled y-axis. `real` is raw dollars (the
    IBKR account's NLV series) and `plan` is an already-indexed nav (starts ~1.0) — comparing raw levels
    would be meaningless, so each series is independently REBASED to start at 1.0 (a cumulative-return
    index) before the shared min/max scaling, exactly like comparing two total-return curves. Aligned by
    POSITION (both are daily series recorded from the same t0); truncated to the shorter series' length.
    Empty (bare `<svg>`) when either series is empty — never a crash on partial data."""
    box = f'<svg width="{width}" height="{height}" viewBox="0 0 {width} {height}" ' \
          'xmlns="http://www.w3.org/2000/svg">'
    if not real or not plan:
        return box + "</svg>"
    real_idx = [v / real[0] for v in real]
    plan_idx = [v / plan[0] for v in plan]
    n = min(len(real_idx), len(plan_idx))
    real_idx = real_idx[:n]
    plan_idx = plan_idx[:n]
    lo, hi = min(real_idx + plan_idx), max(real_idx + plan_idx)
    span = (hi - lo) or 1.0
    step = width / (n - 1) if n > 1 else 0.0

    def _pts(series: list[float]) -> str:
        return " ".join(f"{i * step:.1f},{height - (v - lo) / span * (height - 2) - 1:.1f}"
                        for i, v in enumerate(series))

    return (box
            + f'<polyline fill="none" stroke="{plan_color}" stroke-width="1.5" stroke-dasharray="4,3" '
              f'points="{_pts(plan_idx)}" />'
            + f'<polyline fill="none" stroke="{real_color}" stroke-width="1.5" points="{_pts(real_idx)}" />'
            + "</svg>")


