"""TWAP - Time-Weighted Average Price execution strategy.

Generates a time-based execution schedule to achieve TWAP.
"""
from __future__ import annotations
from datetime import datetime, timedelta
from app.engine.base import BaseAlgorithm
from app.engine.context import AlgorithmContext


class TWAPAlgorithm(BaseAlgorithm):
    algorithm_id = 'behavioral.twap'
    version = '1.0.0'
    category = 'behavioral'
    display_name = 'TWAP Execution'
    dependencies = ['technical.atr']

    def __init__(self, default_duration_hours: float = 4.0, num_slices: int = 8):
        """
        Args:
            default_duration_hours: Execution window duration
            num_slices: Number of equal time slices
        """
        self.default_duration_hours = default_duration_hours
        self.num_slices = num_slices

    def compute(self, ctx: AlgorithmContext) -> AlgorithmContext:
        price = ctx.current_price
        if not price:
            return ctx

        active_capital = ctx.capital_idr * (ctx.active_pct / 100)
        total_order = active_capital * 0.25

        # Adjust duration based on volatility
        atr = ctx.indicators.get('atr_latest', 0)
        atr_pct = (atr / price * 100) if price > 0 else 1

        if atr_pct > 5:
            duration_hours = 1.0   # high vol: compress window
            num_slices = 4
        elif atr_pct > 2:
            duration_hours = 2.0
            num_slices = 6
        else:
            duration_hours = self.default_duration_hours
            num_slices = self.num_slices

        slice_interval = duration_hours * 60 / num_slices  # minutes
        slice_value = total_order / num_slices
        slice_qty = slice_value / price if price > 0 else 0

        # Apply lot rounding for stocks
        slice_qty = ctx.round_to_lot(slice_qty)

        # Build TWAP schedule
        now = datetime.now()
        schedule = []

        # For stocks: constrain execution window to market hours
        if ctx.market_hours:
            open_h, close_h = ctx.market_hours
            # Cap duration to market hours remaining
            market_mins = (close_h - open_h) * 60
            max_duration_mins = duration_hours * 60
            if max_duration_mins > market_mins:
                duration_hours = market_mins / 60
                slice_interval = duration_hours * 60 / num_slices

        for i in range(num_slices):
            exec_time = now + timedelta(minutes=slice_interval * i)

            # Skip slices outside market hours
            if ctx.market_hours:
                open_h, close_h = ctx.market_hours
                if exec_time.hour < open_h or exec_time.hour >= close_h:
                    continue

            schedule.append({
                'slice': i + 1,
                'time': exec_time.strftime('%H:%M'),
                'value_idr': round(slice_value, 0),
                'quantity': float(slice_qty),
                'cumulative_pct': round((i + 1) / num_slices * 100, 1),
            })

        # Calculate TWAP from recent prices
        df = ctx.ohlcv
        twap_price = price
        if df is not None and len(df) >= 5:
            recent = df.tail(int(num_slices))
            twap_price = float(recent['close'].mean())

        deviation_pct = (price - twap_price) / twap_price * 100 if twap_price else 0

        ctx.quantitative['twap'] = {
            'twap_price': ctx.round_price(twap_price),
            'current_vs_twap_pct': round(deviation_pct, 4),
            'is_above_twap': bool(price > twap_price),
            'duration_hours': duration_hours,
            'num_slices': num_slices,
            'slice_interval_mins': round(slice_interval, 1),
            'total_order_value': round(total_order, 0),
            'schedule': schedule,
        }
        return ctx

    def get_signal_contribution(self, ctx: AlgorithmContext):
        twap = ctx.quantitative.get('twap', {})
        if not twap:
            return None

        dev = twap.get('current_vs_twap_pct', 0)
        # Price below TWAP = favorable buy, above = favorable sell
        if dev < -1.0:
            return {'signal': 'BUY', 'weight': 0.05,
                    'reason': f'Price {dev:.1f}% below TWAP - favorable entry'}
        elif dev > 1.0:
            return {'signal': 'SELL', 'weight': 0.05,
                    'reason': f'Price {dev:.1f}% above TWAP - favorable exit'}
        return {'signal': 'HOLD', 'weight': 0.03,
                'reason': f'Price near TWAP ({dev:+.1f}%)'}
