from __future__ import annotations

from dataclasses import dataclass

from investly.domain import Bar, Strategy


@dataclass(frozen=True)
class TargetPlan:
    target: float | None
    stop: float | None
    target_method: str
    invalidation_method: str


def _nearest_resistance(bars: list[Bar], price: float, lookback: int) -> float | None:
    prior = bars[-lookback - 1 : -1] if len(bars) > 1 else []
    highs = sorted({round(bar.high, 8) for bar in prior if bar.high > price})
    return highs[0] if highs else None


def _support(bars: list[Bar], price: float, lookback: int) -> float | None:
    prior = bars[-lookback - 1 : -1] if len(bars) > 1 else []
    supports = [bar.low for bar in prior if 0 < bar.low < price]
    return max(supports) if supports else None


def _measured_move(bars: list[Bar], lookback: int) -> float | None:
    prior = bars[-lookback - 1 : -1] if len(bars) > 1 else []
    if not prior:
        return None
    high = max(bar.high for bar in prior)
    low = min(bar.low for bar in prior)
    width = high - low
    return high + width if width > 0 else None


def target_plan(strategy: Strategy, bars: list[Bar], price: float) -> TargetPlan:
    """Return transparent structure-derived next target and invalidation references.

    Investment fair-value targets require valuation-grade point-in-time fundamentals.
    Until those are present, baseline Investment exposes the next long-term technical
    resistance and explicitly labels fair value as pending rather than inventing one.
    """
    if not bars or price <= 0:
        return TargetPlan(None, None, "unavailable", "unavailable")

    if strategy == Strategy.SWING:
        resistance = _nearest_resistance(bars, price, 63)
        target: float | None
        method: str
        if resistance is not None:
            target = resistance
            method = "technical: nearest prior 63-session resistance"
        else:
            target = _measured_move(bars, 63)
            method = "technical: 63-session measured-move breakout"
        stop = _support(bars, price, 20)
        return TargetPlan(
            target=target if target is not None and target > price else None,
            stop=stop,
            target_method=method,
            invalidation_method="technical: prior 20-session support",
        )

    return TargetPlan(
        target=_nearest_resistance(bars, price, 252),
        stop=_support(bars, price, 63),
        target_method=(
            "technical next target: nearest prior 252-session resistance; "
            "fundamental fair-value target pending valuation-grade data"
        ),
        invalidation_method=(
            "technical risk reference: prior 63-session support plus fundamental thesis review"
        ),
    )
