import logging
import pandas as pd
from datetime import datetime, timedelta
import pytz

logger = logging.getLogger("ICTCore")

class ICTCore:
    """
    Core engine for ICT strategy (default USTECH100M): bias detection, liquidity pool, and FVG scanning.
    """
    def __init__(self):
        self.liquidity_pools = []
        self.bias = None
        self.fvg_signals = []

    def calculate_bias(self, daily_df, hourly_df):
        """
        Calculate composite bias using daily and hourly data.
        Returns: 'bullish', 'bearish', or 'neutral'.
        """
        daily_mid = (daily_df['high'].iloc[-1] + daily_df['low'].iloc[-1]) / 2
        daily_close = daily_df['close'].iloc[-1]
        daily_bias = 1 if daily_close > daily_mid else -1

        hourly_highs = hourly_df['high'].rolling(5).max()
        hourly_lows = hourly_df['low'].rolling(5).min()
        if hourly_highs.iloc[-1] > hourly_highs.iloc[-2]:
            hourly_bias = 1
        elif hourly_lows.iloc[-1] < hourly_lows.iloc[-2]:
            hourly_bias = -1
        else:
            hourly_bias = 0

        composite_score = (daily_bias * 0.7) + (hourly_bias * 0.3)
        if composite_score >= 0.5:
            self.bias = 'bullish'
        elif composite_score <= -0.5:
            self.bias = 'bearish'
        else:
            self.bias = 'neutral'
        logger.info(f"Bias calculated: {self.bias} (score={composite_score})")
        return self.bias

    def detect_liquidity_pools(self, df):
        """
        Detect liquidity pools using 48-hour swing high/low.
        Fallback: previous day high/low, round numbers.
        """
        lookback = 48 * 4  # 48 hours of 15m candles (if M15)
        swings = []
        if len(df) >= lookback:
            highs = df['high'].rolling(window=lookback, min_periods=1).max()
            lows = df['low'].rolling(window=lookback, min_periods=1).min()
            swings = list(set([highs.iloc[-1], lows.iloc[-1]]))
        # Fallbacks
        if not swings:
            if len(df) >= 96:  # 1 day of M15
                swings = [df['high'].iloc[-96], df['low'].iloc[-96]]
        # Add round numbers
        price = df['close'].iloc[-1]
        round_levels = [round(price/5)*5, round(price/10)*10]
        self.liquidity_pools = swings + round_levels
        logger.info(f"Liquidity pools detected: {self.liquidity_pools}")
        return self.liquidity_pools

    def detect_fvg(self, df):
        """
        Detect Fair Value Gaps (FVG) with 3-candle lookback, 0.15% gap, within 5 candles post-sweep.
        Returns list of FVG dicts.
        """
        fvg_signals = []
        for i in range(2, len(df)):
            c1, c2, c3 = df.iloc[i-2], df.iloc[i-1], df.iloc[i]
            gap = abs(c1['high'] - c3['low']) / c3['low']
            if (c1['high'] < c3['low']) and (c2['low'] < c1['high']) and gap >= 0.0015:
                fvg_signals.append({'type': 'bullish', 'index': i, 'gap': gap})
            gap = abs(c1['low'] - c3['high']) / c3['high']
            if (c1['low'] > c3['high']) and (c2['high'] > c1['low']) and gap >= 0.0015:
                fvg_signals.append({'type': 'bearish', 'index': i, 'gap': gap})
        self.fvg_signals = fvg_signals
        logger.info(f"FVGs detected: {self.fvg_signals}")
        return fvg_signals

    def detect_sweep(self, df, threshold=0.003):
        """
        Detects a valid liquidity sweep: price exceeds 4-hour high/low by 0.3%.
        Returns 'bullish', 'bearish', or None.
        """
        lookback = 16  # 4 hours of M15 candles
        logger.info(f"[SWEEP DEBUG] Data length: {len(df)}, Lookback required: {lookback}")
        if len(df) < lookback:
            logger.info(f"[SWEEP DEBUG] Not enough data for sweep detection. Data len: {len(df)}, required: {lookback}")
            return None
        recent_high = df['high'].iloc[-lookback:-1].max()
        recent_low = df['low'].iloc[-lookback:-1].min()
        current_price = df['close'].iloc[-1]
        logger.info(f"[SWEEP DEBUG] recent_high={recent_high}, recent_low={recent_low}, current_price={current_price}, threshold={threshold}")
        if current_price > recent_high * (1 + threshold):
            logger.info(f"Bullish sweep detected: {current_price} > {recent_high} * (1+{threshold})")
            return 'bullish'
        elif current_price < recent_low * (1 - threshold):
            logger.info(f"Bearish sweep detected: {current_price} < {recent_low} * (1-{threshold})")
            return 'bearish'
        logger.info("No sweep detected.")
        return None

    def get_session_and_size(self, dt_utc):
        """
        Determines session, position size multiplier, and stop buffer based on EST time.
        Returns (session_name, size_multiplier, stop_buffer)
        """
        est = pytz.timezone('US/Eastern')
        dt_est = dt_utc.astimezone(est)
        hour = dt_est.hour
        minute = dt_est.minute
        t = hour + minute/60
        # Session logic - all sessions use 1.0 multiplier for strict 1% risk
        if 2 <= t < 5:
            logger.info("London session (2-5AM EST): full size (strict 1% risk)")
            return ('London', 1.0, 0.0)
        elif 8 <= t < 11:
            logger.info("NY session (8-11AM EST): full size (strict 1% risk)")
            return ('NY', 1.0, 0.0)
        elif 19 <= t or t < 1:
            logger.info("Asian session (7PM-1AM EST): full size (strict 1% risk)")
            return ('Asian', 1.0, 0.0)
        else:
            logger.info("Other session: full size (strict 1% risk)")
            return ('Other', 1.0, 0.0) 