"""WP + Ichimoku Combined Strategy — adapted for Vanta Crunch.

Wraps the CombinedWPIchimokuStrategy from the trading_strategy package
into a TrackerBase that works with incremental candle delivery.

The strategy uses:
- WP Levels: Fibonacci-based support/resistance (50%, thirds, quarters)
- WP Pivots: 7-to-28 cycle timing for reversals
- Four Touch Rule: Pattern breakout confirmation (4 touches → breakout)
- Ichimoku Cloud: TK cross, Kumo position, Chikou span, cloud color
- Heikin Ashi: Momentum confirmation via flat-top/flat-bottom candles

Entry requires confluence from BOTH WP and Ichimoku systems.
Signal strength scales with confluence count.
"""

from __future__ import annotations

import sys
from pathlib import Path

import numpy as np
import pandas as pd

from vanta.tracker import TrackerBase

# Add trading_strategy indicators to path directly (avoids importing
# the full package which pulls in httpx and other runtime deps)
_TS_SRC = Path.home() / "workspace" / "trading_strategy" / "src"
_TS_IND = _TS_SRC / "trading_strategy" / "indicators"
_TS_SIG = _TS_SRC / "trading_strategy" / "signals"
_TS_STR = _TS_SRC / "trading_strategy" / "strategies"

# We import the modules directly to avoid the package __init__ chain
import importlib.util


def _load_module(name: str, path: Path):
    spec = importlib.util.spec_from_file_location(name, path)
    mod = importlib.util.module_from_spec(spec)
    sys.modules[name] = mod
    spec.loader.exec_module(mod)
    return mod


# Load signal types first (dependency of combined strategy)
_types_mod = _load_module("trading_strategy.signals.types", _TS_SIG / "types.py")

# Load indicators
_ichimoku_mod = _load_module("trading_strategy.indicators.ichimoku", _TS_IND / "ichimoku.py")
_wp_levels_mod = _load_module("trading_strategy.indicators.wp_levels", _TS_IND / "wp_levels.py")
_wp_pivots_mod = _load_module("trading_strategy.indicators.wp_pivots", _TS_IND / "wp_pivots.py")
_four_touch_mod = _load_module("trading_strategy.indicators.four_touch", _TS_IND / "four_touch.py")

# Load combined strategy
_combined_mod = _load_module(
    "trading_strategy.strategies.combined_wp_ichimoku", _TS_STR / "combined_wp_ichimoku.py"
)

CombinedWPIchimokuStrategy = _combined_mod.CombinedWPIchimokuStrategy


class WPIchimokuTracker(TrackerBase):
    """WP + Ichimoku combined strategy for Vanta Crunch.

    Accumulates 1-hour candles into a DataFrame, then runs the full
    CombinedWPIchimokuStrategy calculation on each predict() call.

    The strategy needs ~78 candles for full Ichimoku warmup (52 + 26
    displacement), so predictions start after that.

    Args:
        min_wp_confluence: Minimum WP factors for entry (default 2).
        min_ichimoku_confluence: Minimum Ichimoku factors for entry (default 2).
        min_total_confluence: Minimum total factors for entry (default 4).
            Lowered from default 5 to generate enough signals for backtesting.
        wp_levels_rolling: Rolling window for WP level calculation (default 52).
        signal_scale: Multiplier for signal magnitude (default 0.01).
    """

    def __init__(
        self,
        min_wp_confluence: int = 2,
        min_ichimoku_confluence: int = 2,
        min_total_confluence: int = 4,
        wp_levels_rolling: int = 52,
        signal_scale: float = 0.01,
    ):
        super().__init__()
        self.signal_scale = signal_scale
        self._candles: list[dict] = []
        self._last_calc_len: int = 0
        self._cached_signal: float = 0.0

        # We'll create the strategy lazily so params are captured
        self._strategy = CombinedWPIchimokuStrategy(
            wp_levels_rolling=wp_levels_rolling,
            min_wp_confluence=min_wp_confluence,
            min_ichimoku_confluence=min_ichimoku_confluence,
            min_total_confluence=min_total_confluence,
            use_heikin_ashi=True,
        )
        # Warmup: need senkou_b_period(52) + displacement(26) = 78 candles
        self._warmup = 78

    def tick(self, data: dict):
        super().tick(data)
        # Accumulate 1h candles (best timeframe for Ichimoku)
        for c in data.get("candles_1h", []):
            self._candles.append({
                "ts": c["ts"],
                "open": float(c["open"]),
                "high": float(c["high"]),
                "low": float(c["low"]),
                "close": float(c["close"]),
                "volume": float(c.get("volume", 0)),
            })

    def predict(self, symbol: str, horizon_seconds: int, step_seconds: int) -> dict:
        if len(self._candles) < self._warmup:
            return {"expected_return": 0.0}

        # Only recalculate if new candles have arrived
        if len(self._candles) == self._last_calc_len:
            return {"expected_return": self._cached_signal}

        # Build DataFrame from last N candles (full history is too slow
        # for four_touch trendline fitting). 200 candles covers Ichimoku
        # warmup (78) plus enough history for WP levels and pivots.
        max_window = 200
        recent = self._candles[-max_window:] if len(self._candles) > max_window else self._candles
        df = pd.DataFrame(recent)
        df.index = pd.RangeIndex(len(df))

        try:
            # Run full strategy calculation
            result_df = self._strategy.calculate(df)
            current = result_df.iloc[-1]

            # Extract confluence scores
            bull = int(current.get("total_bullish", 0))
            bear = int(current.get("total_bearish", 0))

            # Convert to expected_return signal
            # Positive = bullish, negative = bearish
            # Scale by confluence strength
            if bull > bear and bull >= self._strategy.min_total_confluence:
                # Bullish entry signal — scale by how much above threshold
                strength = bull / 9.0  # max possible ~9 factors
                signal = strength * self.signal_scale
            elif bear > bull and bear >= self._strategy.min_total_confluence:
                # Bearish entry signal
                strength = bear / 9.0
                signal = -strength * self.signal_scale
            elif bull > bear:
                # Weak bullish bias (below entry threshold)
                signal = (bull - bear) / 9.0 * self.signal_scale * 0.3
            elif bear > bull:
                # Weak bearish bias
                signal = -(bear - bull) / 9.0 * self.signal_scale * 0.3
            else:
                signal = 0.0

            # Check for exhaustion — reduce signal
            if current.get("nine_candle_exhaustion", False) or current.get("exit_exhaustion", False):
                signal *= 0.2  # Dramatically reduce conviction

            self._cached_signal = max(-0.05, min(0.05, signal))
            self._last_calc_len = len(self._candles)
            return {"expected_return": self._cached_signal}

        except Exception:
            return {"expected_return": 0.0}
