"""General candlestick pattern recognition."""
from __future__ import annotations
from app.engine.base import BaseAlgorithm
from app.engine.context import AlgorithmContext


class CandlestickAlgorithm(BaseAlgorithm):
    algorithm_id = 'patterns.candlestick'
    version = '1.0.0'
    category = 'patterns'
    display_name = 'Candlestick Patterns'
    dependencies = []

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

        o = df['open'].values.astype(float)
        h = df['high'].values.astype(float)
        l = df['low'].values.astype(float)
        c = df['close'].values.astype(float)
        i = len(c) - 1  # Latest candle

        rng = h[i] - l[i]
        body = abs(c[i] - o[i])
        upper_shadow = h[i] - max(o[i], c[i])
        lower_shadow = min(o[i], c[i]) - l[i]

        detected = []

        if rng > 0:
            body_ratio = body / rng

            # Doji: very small body
            if body_ratio < 0.1 and rng > 0:
                detected.append('doji')

            # Hammer: small body at top, long lower shadow
            if lower_shadow > body * 2 and upper_shadow < body * 0.5 and c[i] > o[i]:
                detected.append('hammer')

            # Inverted Hammer
            if upper_shadow > body * 2 and lower_shadow < body * 0.5 and c[i] > o[i]:
                detected.append('inverted_hammer')

            # Shooting Star
            if upper_shadow > body * 2 and lower_shadow < body * 0.5 and c[i] < o[i]:
                detected.append('shooting_star')

            # Hanging Man
            if lower_shadow > body * 2 and upper_shadow < body * 0.5 and c[i] < o[i]:
                detected.append('hanging_man')

        # Two-candle patterns
        if i >= 1:
            prev_body = abs(c[i - 1] - o[i - 1])
            prev_bullish = c[i - 1] > o[i - 1]

            # Engulfing
            if prev_body > 0:
                if not prev_bullish and c[i] > o[i] and body > prev_body * 1.2:
                    if o[i] <= c[i - 1] and c[i] >= o[i - 1]:
                        detected.append('bullish_engulfing')
                elif prev_bullish and c[i] < o[i] and body > prev_body * 1.2:
                    if o[i] >= c[i - 1] and c[i] <= o[i - 1]:
                        detected.append('bearish_engulfing')

        # Three-candle: Morning/Evening Star
        if i >= 2:
            first_body = abs(c[i - 2] - o[i - 2])
            mid_body = abs(c[i - 1] - o[i - 1])

            if first_body > 0 and mid_body < first_body * 0.3 and body > first_body * 0.5:
                if c[i - 2] < o[i - 2] and c[i] > o[i]:
                    detected.append('morning_star')
                elif c[i - 2] > o[i - 2] and c[i] < o[i]:
                    detected.append('evening_star')

        ctx.patterns['candlestick'] = {
            'detected': detected,
            'count': len(detected),
        }
        return ctx

    def get_signal_contribution(self, ctx: AlgorithmContext) -> dict | None:
        p = ctx.patterns.get('candlestick')
        if not p or not p['detected']:
            return None

        bullish = {'hammer', 'inverted_hammer', 'bullish_engulfing', 'morning_star'}
        bearish = {'shooting_star', 'hanging_man', 'bearish_engulfing', 'evening_star'}
        neutral = {'doji'}

        found = set(p['detected'])
        bull_found = found & bullish
        bear_found = found & bearish

        if bull_found:
            return {'signal': 'BUY', 'weight': 0.10,
                    'reason': f"Bullish patterns: {', '.join(bull_found)}"}
        elif bear_found:
            return {'signal': 'SELL', 'weight': 0.10,
                    'reason': f"Bearish patterns: {', '.join(bear_found)}"}
        elif found & neutral:
            return {'signal': 'HOLD', 'weight': 0.03,
                    'reason': 'Doji — indecision'}
        return None
