"""Support & Resistance detection with S/R Flip."""
from __future__ import annotations
import numpy as np
from app.engine.base import BaseAlgorithm
from app.engine.context import AlgorithmContext


class SupportResistanceAlgorithm(BaseAlgorithm):
    algorithm_id = 'technical.support_resistance'
    version = '1.0.0'
    category = 'technical'
    display_name = 'Support & Resistance'
    dependencies = []

    def __init__(self, window: int = 20, cluster_pct: float = 0.02):
        self.window = window
        self.cluster_pct = cluster_pct

    def _cluster_levels(self, levels: list[float]) -> list[float]:
        if not levels:
            return []
        levels = sorted(levels)
        clusters = [[levels[0]]]
        for lev in levels[1:]:
            if abs(lev - clusters[-1][-1]) / clusters[-1][-1] < self.cluster_pct:
                clusters[-1].append(lev)
            else:
                clusters.append([lev])
        return [float(np.mean(c)) for c in clusters]

    def compute(self, ctx: AlgorithmContext) -> AlgorithmContext:
        df = ctx.ohlcv
        if df is None or len(df) < self.window * 2:
            return ctx

        closes = df['close'].values.astype(float)
        support_raw, resistance_raw = [], []

        for i in range(self.window, len(closes) - self.window):
            local_window = closes[i - self.window:i + self.window + 1]
            if closes[i] == np.min(local_window):
                support_raw.append(closes[i])
            if closes[i] == np.max(local_window):
                resistance_raw.append(closes[i])

        supports = self._cluster_levels(support_raw)
        resistances = self._cluster_levels(resistance_raw)

        ctx.indicators['support_levels'] = supports
        ctx.indicators['resistance_levels'] = resistances

        # Nearest support and resistance to current price
        price = ctx.current_price or closes[-1]
        nearest_support = max([s for s in supports if s < price], default=None)
        nearest_resistance = min([r for r in resistances if r > price], default=None)
        ctx.indicators['nearest_support'] = nearest_support
        ctx.indicators['nearest_resistance'] = nearest_resistance

        # S/R Flip detection: former resistance now acting as support (or vice versa)
        sr_flip = None
        if nearest_support and resistances:
            for r in resistances:
                if abs(r - nearest_support) / nearest_support < self.cluster_pct:
                    sr_flip = 'resistance_to_support'
                    break
        if nearest_resistance and supports:
            for s in supports:
                if abs(s - nearest_resistance) / nearest_resistance < self.cluster_pct:
                    sr_flip = 'support_to_resistance'
                    break

        ctx.indicators['sr_flip'] = sr_flip
        return ctx

    def get_signal_contribution(self, ctx: AlgorithmContext) -> dict | None:
        price = ctx.current_price
        support = ctx.indicators.get('nearest_support')
        resistance = ctx.indicators.get('nearest_resistance')
        sr_flip = ctx.indicators.get('sr_flip')

        if not price or not support or not resistance:
            return None

        dist_support_pct = (price - support) / price * 100
        dist_resistance_pct = (resistance - price) / price * 100

        # Near support = potential buy
        if dist_support_pct < 2:
            weight = 0.12 if sr_flip == 'resistance_to_support' else 0.08
            return {'signal': 'BUY', 'weight': weight,
                    'reason': f'Price near support {support:.2f} ({dist_support_pct:.1f}% away)'
                              + (' + S/R Flip confirmed' if sr_flip else '')}
        # Near resistance = potential sell
        elif dist_resistance_pct < 2:
            weight = 0.12 if sr_flip == 'support_to_resistance' else 0.08
            return {'signal': 'SELL', 'weight': weight,
                    'reason': f'Price near resistance {resistance:.2f} ({dist_resistance_pct:.1f}% away)'
                              + (' + S/R Flip confirmed' if sr_flip else '')}

        return None
